前言sql
CoreData不是數據庫,而是對象模型,它提供了對象-關係映射(ORM)功能,能將OC對象保存到SQLite數據庫文件中,也能夠將數據庫數據還原成OC對象;不須要掌握SQL語法也能夠操做數據庫,有點相似.Net的EF框架。數據庫
1、CoreData須要使用幾個對象進行操做。NSManagedObjectModel對象,加載模型文件,讀取app中的全部實體信息;NSPersistentStoreCoordinator對象,添加持久化庫(這裏採起SQLite數據庫);NSManagedObjectContext對象,拿到這個上下文對象操做實體,進行CRUD操做。若是你是在建立項目的時候就勾選了CoreData,那麼會在AppDelegate文件中自動生成相關代碼,這裏採起自定義封裝類的方法,獨立封裝成單例類。安全
#import <Foundation/Foundation.h> #import <CoreData/CoreData.h> @interface Database : NSObject + (Database *)database; - (void)saveContext; @property (nonatomic, strong, readonly) NSManagedObjectContext *objectContext; @property (nonatomic, strong, readonly) NSManagedObjectModel *objectModel; @property (nonatomic, strong, readonly) NSPersistentStoreCoordinator *store; @end #import "Database.h" @implementation Database @synthesize objectContext = _objectContext; @synthesize objectModel = _objectModel; @synthesize store = _store; - (void)saveContext { NSError *saveError = nil; NSManagedObjectContext *context = self.objectContext; if ([context hasChanges] && ![context save:&saveError]) { if (saveError) { NSLog(@"save error -- %@", [saveError userInfo]); abort(); } } } + (Database *)database { static Database *database; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ database = [[Database alloc] init]; }); return database; } // 建立模型對象 - (NSManagedObjectModel *)objectModel { if (!_objectModel) { NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"ZHModel" withExtension:@"momd"]; _objectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; } return _objectModel; } // 建立管理模型的上下文對象 - (NSManagedObjectContext *)objectContext { if (!_objectContext) { _objectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; [_objectContext setPersistentStoreCoordinator:self.store]; } return _objectContext; } // 建立持久化存儲協調器 - (NSPersistentStoreCoordinator *)store { if (!_store) { // 設置sqlite本地文件存放地址; NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"ZHModel.sqlite"]; NSError *error = nil; NSLog(@"path -- %@", storeURL); // 須要數據遷移時,所設置的選項; NSDictionary *optionDic = @{ NSMigratePersistentStoresAutomaticallyOption: @(YES), NSInferMappingModelAutomaticallyOption: @(NO) }; _store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.objectModel]; // 此處將NSPersistentStoreCoordinator對象做爲對象模型與本地sqlite文件中間的協調器; [_store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:optionDic error:&error]; if (error) { NSLog(@"store error -- %@", [error userInfo]); abort(); } } return _store; } // sqlite文件存放目錄; - (NSURL *)applicationDocumentsDirectory { return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; } @end
2、CoreData並非線程安全的,對於ManagedObject以及ManagedObjectContext的訪問都只能在對應的線程上進行,而不能誇線程。並行方案也有不少種,性能各有不一樣,一種是Notification方式(簡單來講,就是不一樣的線程使用不一樣的context進行操做,當一個線程的context發生變化後,利用notification來通知另外一個線程Context,另外一個線程調用mergeChangesFromContextDidSaveNotification來合併變化);另外一種是child/parent context方式(ChildContext和ParentContext是相互獨立的。只有當ChildContext中調用Save了之後,纔會把這段時間來Context的變化提交到ParentContext中,ChildContext並不會直接提交到NSPersistentStoreCoordinator中, parentContext就至關於它的NSPersistentStoreCoordinator),當ParentContext執行save後,纔會把Context的變化保存到本地文件。今天我所討論的是Notification方式,給出幾個關鍵的地方,Notification方式是共用NSPersistentStoreCoordinator的;app
self.mainContext = [Database database].objectContext;// 運行在主隊列的context self.privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];// 運行在私有隊列的context [self.privateContext setPersistentStoreCoordinator:_mainContext.persistentStoreCoordinator]; // 二者共用着一個persistentStoreCoordinator
一、並行的解決方案之Notification第一步,子線程中的context添加數據並保存,執行insertData方法。框架
// 在私有隊列中添加保存數據,添加成功後會發出NSManagedObjectContextDidSaveNotification通知; - (void)insertData { TestEntity2 *testEntity = [NSEntityDescription insertNewObjectForEntityForName:@"TestEntity2" inManagedObjectContext:self.privateContext]; testEntity.title = [NSString stringWithFormat:@"entity%u", arc4random() % 100]; [self.privateContext performBlock:^{ NSError *saveError = nil; if ([self.privateContext hasChanges]) { [self.privateContext save:&saveError]; } }]; }
二、privateContext保存成功後,發出NSManagedObjectContextDidSaveNotification通知,mainContext經過mergeChangesFromContextDidSaveNotification合併context的變化。dom
// 接收到數據變化保存的通知,主隊列執行合併操做。 mergeObject = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) { if ([note.object isEqual:self.privateContext]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.mainContext performBlock:^{ [self.mainContext mergeChangesFromContextDidSaveNotification:note]; [self fetchData]; }]; }); } }];
總結async
持久化存儲框架CoreData,FMDB,二者在項目中應用普遍,但不一樣之處也很明顯。性能
一、CoreData不是線程安全的,而FMDB是線程安全的(使用FMDatabaseQueue);fetch
二、個人理解CoreData數據遷移的不一樣,好比數據庫表的結構刪除字段或者是修改字段名,因爲SQLite不支持刪除數據表的列以及修改字段名,因此FMDB須要編寫SQL語句邏輯(從新建立新表、而後填充舊數據、刪除舊錶以及修改新表名稱),CoreData則在新的對象模型中作新舊錶的字段對應和刪除對象模型的屬性。編碼
三、本人喜歡使用FMDB,緣由很簡單就是喜歡比較透明的編碼方式。