基本結(jié)構(gòu)
CoreData相當(dāng)于一個(gè)綜合的數(shù)據(jù)存儲(chǔ)和管理中心财喳,它支持sqlite察迟,二進(jìn)制存儲(chǔ)文件兩種形式的數(shù)據(jù)存儲(chǔ)。而CoreData提供了存儲(chǔ)管理耳高,包括查詢(xún)扎瓶、插入、刪除泌枪、更新概荷、回滾、會(huì)話(huà)管理碌燕、鎖管理等一系列數(shù)據(jù)庫(kù)操作误证。另外,開(kāi)發(fā)者還可以在Xcode中使用 .xcdatamodel 擴(kuò)展名的文件修壕,以圖形化的形式編輯數(shù)據(jù)模型愈捅,這里包括Entities、Properties慈鸠、Attributes蓝谨、Relationships四個(gè)概念,這里跟關(guān)系型數(shù)據(jù)庫(kù)有很大的相似點(diǎn)。
下面來(lái)看一下CoreData的框架圖譬巫。
常用類(lèi)型
一次了解一下 **PersistentStore咖楣、DataModel、PersistentStoreCoordinator缕题、ManagedObjects截歉、ManagedObjectsContext、FetchRequest **這些概念烟零。
- 1.PersistentStore
這個(gè)是數(shù)據(jù)真正存儲(chǔ)的地方瘪松,CodeData提供了兩種存儲(chǔ)的選擇,分別是sqlite和二進(jìn)制文件锨阿。PersistentStore本身并不是objc類(lèi)宵睦,僅僅是數(shù)據(jù)存儲(chǔ)。 - 2.DataModel
對(duì)應(yīng)的objc類(lèi)為 NSManagedObjectModel墅诡,一個(gè)典型的應(yīng)用如:
//Returns the managed object model for the application.
If the model doesn't already exist, it is created by merging all of the models
found in the application bundle.
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
return managedObjectModel;
}
// 這里用了iPhone開(kāi)發(fā)中典型的laze loading壳嚎,
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
// 參數(shù)中的nil表示連接項(xiàng)目中所有的 .xcodemodel 文件為一個(gè)datamodel,這是一個(gè)非常好的方法末早,
// 把多個(gè)entity放在各自的xcodemodel文件中分開(kāi)管理烟馅,然后用這個(gè)函數(shù)連接起來(lái)生成一個(gè)datamodel,這樣就可以對(duì)應(yīng)一個(gè)persistentStore然磷。
- 3.PersistentStoreCoordinator
對(duì)應(yīng)的objc類(lèi)為NSPersistentStoreCoordinator郑趁,這個(gè)類(lèi)用來(lái)控制對(duì)PersistentStore的訪(fǎng)問(wèn)。PersistentStoreCoordinator提供了一些列的高級(jí)調(diào)用供其他類(lèi)來(lái)使用姿搜,對(duì)PersistentStore進(jìn)行讀和寫(xiě),比如增查刪改寡润。下面看一段典型的代碼:
//Returns the persistent store coordinator for the application.
If the coordinator doesn't already exist, it is created and the application's store
added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: @"CoreData.sqlite"]];
NSError *error;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel: [self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:nil error:&error]) {
// Handle error
}
return persistentStoreCoordinator;
}
這里默認(rèn)存儲(chǔ)形式為sqlite,并且存儲(chǔ)文件為CoreData.sqlite舅柜,這段代碼比較簡(jiǎn)單卓缰,創(chuàng)建了persistentStoreCoordinator實(shí)例卸勺。
- 4.ManagedObjects
對(duì)應(yīng)的objc類(lèi)為NSManagedObject拇舀。上面的CoreData框架圖中有Entities奔缠,Entity定義了數(shù)據(jù)的結(jié)構(gòu),但他并不是數(shù)據(jù)知举,真正的數(shù)據(jù)實(shí)例是NSManagedObject類(lèi)或他的子類(lèi)瞬沦。
NSManagedObject類(lèi)支持Key-Value 編碼(KVC),像NSDictionary差不多雇锡。NSManagedObject提供了valueForKey:和setValue:forKey:用來(lái)設(shè)置和查詢(xún)的方法。另外他也提供了對(duì)關(guān)系操作的方法僚焦。
下面是幾個(gè)典型的代碼案例:
NSDate *timeStamp = [managedObject valueForKey:@"timeStamp"];
[managedObject setValue:[NSDate date] forKey:@"timeStamp"];
另外KVC也支持keypath锰提,如有兩個(gè)數(shù)據(jù)entity,一個(gè)是Employee,一個(gè)事Employer立肘,Employee中有個(gè)屬性石whereIWork边坤,而這個(gè)屬性用relationship連接到了對(duì)應(yīng)的Employer,Employer中有個(gè)屬性石name谅年,這樣要查詢(xún)一個(gè)Employer的name茧痒,可以用keypath的形式,whereIWork.name
融蹂。
NSString *employerName = [managedObject valueForKeyPath:@"whereIWork.name"];
- 5.ManagedObjectsContext
對(duì)應(yīng)的objc類(lèi)為NSManagedObjectsContext旺订。 這個(gè)類(lèi)是一個(gè)用戶(hù)對(duì)persistentStore操作的網(wǎng)關(guān),他維護(hù)了用戶(hù)創(chuàng)建或者加載的managed objects超燃。他記錄了用戶(hù)對(duì)managed objects的所有改變区拳,以便用來(lái)undo或redo,另外當(dāng)用戶(hù)要存儲(chǔ)現(xiàn)在的managed objects到persistentstore時(shí)意乓,只需調(diào)用managedObjectsContext的save方法就行了樱调。
每個(gè)應(yīng)用至少需要一個(gè)context,當(dāng)然可以同時(shí)存在多個(gè)context届良,比如多線(xiàn)程時(shí)笆凌,如NSOperationQueue。context并不是線(xiàn)程安全的士葫,因此在這種情況中用戶(hù)要自己做好安全工作乞而。
下面是一個(gè)簡(jiǎn)單應(yīng)用實(shí)例。
/**
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent
store coordinator for the application.
*/
- (NSManagedObjectContext *) managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
這個(gè)代碼也比較簡(jiǎn)單为障,不做解釋了晦闰。
FetchRequest(FetchRequestController)
這里重點(diǎn)講FetchRequestController,其實(shí)用戶(hù)打交道最多的就是這個(gè)控制器了鳍怨。 對(duì)應(yīng)的objc類(lèi)為NSFetchedResultsController呻右。這個(gè)類(lèi)是用來(lái)管理CoreData Fetch request返回的對(duì)象的。
在創(chuàng)建這個(gè)控制器之前鞋喇,必須先創(chuàng)建fetch request声滥。 fetch request描述了詳細(xì)的查詢(xún)規(guī)則,還可以添加查詢(xún)結(jié)果的排序描述(sort descriptor)侦香。fetchResultsController根據(jù)已經(jīng)創(chuàng)建完的fetch request來(lái)創(chuàng)建落塑, 它是NSFetchedResultsController的實(shí)例,這個(gè)實(shí)例的主要任務(wù)就是使用fetch request來(lái)保證它所關(guān)聯(lián)的數(shù)據(jù)的新鮮性罐韩。創(chuàng)建了fetchResultsController實(shí)例后要做一下初始化憾赁,一般初始化是向這個(gè)控制器發(fā)送PerformFetch消息,下面是這一過(guò)程的代碼散吵。
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event"
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey:@"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor,nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:nil
cacheName:@"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
return fetchedResultsController;
}
這個(gè)函數(shù)用來(lái)創(chuàng)建FetchedResultsController龙考,過(guò)程還是比較簡(jiǎn)單的蟆肆,下面是初始化這個(gè)控制器代碼。
NSError *error = nil;
if(![[self fetchedResultsController]performFetch: &error]){
//handle the error appropriately
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1);
}
這段代碼一般會(huì)放在viewDidLoad函數(shù)中晦款,初始化之后炎功,fetchedResultsController就與數(shù)據(jù)相連接了,之后要取數(shù)據(jù)都能直接從這個(gè)控制器提供的方法中去取缓溅。
實(shí)現(xiàn)這個(gè)控制器蛇损,最關(guān)鍵的還要實(shí)現(xiàn)Fetched Results Controller Delegate Methods√彻郑控制器與數(shù)據(jù)源連接后淤齐,控制器監(jiān)視器會(huì)時(shí)刻監(jiān)視著數(shù)據(jù)源,當(dāng)數(shù)據(jù)源發(fā)生
改變后酝陈,監(jiān)視器會(huì)調(diào)用對(duì)應(yīng)的協(xié)議方法床玻,改協(xié)議總共要實(shí)現(xiàn)四個(gè)方法,分別為:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller沉帮;
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller锈死;
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath;
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type穆壕;
下面依次來(lái)解釋這四個(gè)協(xié)議方法待牵。
*1. - (void)controllerWillChangeContent:(NSFetchedResultsController )controller
當(dāng)控制器監(jiān)控的數(shù)據(jù)發(fā)生改變時(shí),如對(duì)象被刪除喇勋,有插入缨该,更新等,監(jiān)視器會(huì)在數(shù)據(jù)發(fā)生改變前意識(shí)到這個(gè)情況川背,此時(shí)就會(huì)調(diào)用這個(gè)函數(shù)贰拿。往往我們用列表的形式表現(xiàn)數(shù)據(jù),此時(shí)意味著屏幕上的數(shù)據(jù)即將過(guò)時(shí)熄云,因?yàn)閿?shù)據(jù)馬上要改變了膨更,這是這個(gè)協(xié)議方法的工作就是通知列表數(shù)據(jù)馬上要更新的消息,往往代碼是這樣實(shí)現(xiàn)的缴允。
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
*2. - (void)controllerDidChangeContent:(NSFetchedResultsController )controller
當(dāng)fetchedResultsController完成對(duì)數(shù)據(jù)的改變時(shí)荚守,監(jiān)視器會(huì)調(diào)用這個(gè)協(xié)議方法。在上面提到的情況练般,這個(gè)方法要通知列表數(shù)據(jù)已經(jīng)完成矗漾,可以更新顯示的數(shù)據(jù)這個(gè)消息,因此通常的實(shí)現(xiàn)是這樣的薄料。
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
** 3. - (void)controller:(NSFetchedResultsController )controller*
** didChangeObject:(id)anObject **
** atIndexPath:(NSIndexPath )indexPath **
** forChangeType:(NSFetchedResultsChangeType)type **
** newIndexPath:(NSIndexPath )newIndexPath
當(dāng)fetchedResultsController發(fā)現(xiàn)指定的對(duì)象有改變時(shí)敞贡,監(jiān)視器會(huì)調(diào)用這個(gè)協(xié)議方法。這里改變的類(lèi)型從列表中體現(xiàn)有 更新摄职、插入嫡锌、刪除或者行的移動(dòng)虑稼。因此這個(gè)方法要實(shí)現(xiàn)所有的這些方法琳钉,以應(yīng)對(duì)任何一種改變势木。下面是這個(gè)方法的標(biāo)準(zhǔn)實(shí)現(xiàn)。
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate: {
NSString *sectionKeyPath = [controller sectionNameKeyPath];
if (sectionKeyPath == nil)
break;
NSManagedObject *changedObject = [controller objectAtIndexPath:indexPath];
NSArray *keyParts = [sectionKeyPath componentsSeparatedByString:@"."];
id currentKeyValue = [changedObject valueForKeyPath:sectionKeyPath];
for (int i = 0; i < [keyParts count] - 1; i++) {
NSString *onePart = [keyParts objectAtIndex:i];
changedObject = [changedObject valueForKey:onePart];
}
sectionKeyPath = [keyParts lastObject];
NSDictionary *committedValues = [changedObject committedValuesForKeys:nil];
if ([[committedValues valueForKeyPath:sectionKeyPath]isEqual:currentKeyValue])
break;
NSUInteger tableSectionCount = [self.tableView numberOfSections];
NSUInteger frcSectionCount = [[controller sections] count];
if (tableSectionCount != frcSectionCount) {
// Need to insert a section
NSArray *sections = controller.sections;
NSInteger newSectionLocation = -1;
for (id oneSection in sections) {
NSString *sectionName = [oneSection name];
if ([currentKeyValue isEqual:sectionName]) {
newSectionLocation = [sections indexOfObject:oneSection];
break;
}
}
if (newSectionLocation == -1)
return; // uh oh
if (!((newSectionLocation == 0) && (tableSectionCount == 1)
&& ([self.tableView numberOfRowsInSection:0] == 0)))
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:newSectionLocation]
withRowAnimation:UITableViewRowAnimationFade];
NSUInteger indices[2] = {newSectionLocation, 0};
newIndexPath = [[[NSIndexPath alloc] initWithIndexes:indiceslength:2] autorelease];
}
}
case NSFetchedResultsChangeMove
if (newIndexPath != nil) {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:newIndexPath]
withRowAnimation: UITableViewRowAnimationRight];
}
else {
[self.tableView reloadSections:[NSIndexSet
indexSetWithIndex:[indexPath section]]withRowAnimation:UITableViewRowAnimationFade];
}
break;
default:
break;
}
}
從上面的代碼可以看出歌懒,插入啦桌,刪除,移動(dòng)是比較簡(jiǎn)單的及皂,最復(fù)雜的是更新甫男。這個(gè)代碼是xcode的模板代碼,基本能適用我們遇到的情況.
** 4. - (void)controller:(NSFetchedResultsController )controller*
** didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo**
** atIndex:(NSUInteger)sectionIndex **
** forChangeType:(NSFetchedResultsChangeType)type**
當(dāng)改變控制器管理的對(duì)象后引起了列表section的變化验烧,此時(shí)監(jiān)視器就會(huì)調(diào)用這個(gè)協(xié)議函數(shù)板驳。
下面是標(biāo)準(zhǔn)實(shí)現(xiàn)。
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
if (!((sectionIndex == 0) && ([self.tableView numberOfSections] == 1)
&& ([self.tableView numberOfRowsInSection:0] == 0)))
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
if (!((sectionIndex == 0) && ([self.tableView numberOfSections] == 1)
&& ([self.tableView numberOfRowsInSection:0] == 0)))
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
case NSFetchedResultsChangeUpdate:
default:
break;
}
}
參考鏈接
CoreData之FetchRequestController
CoreData入門(mén)
深入淺出 Cocoa 之 Core Data
XMPP介紹二:Core Data