前言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,缘由很简单就是喜欢比较透明的编码方式。