Core Data是iOS中很重要的一个部分,能够理解为基于SQLite(固然也能够是其余的Storage,如In-memory,只是SQLite比较常见)的一个ORM实现,因此有关系数据库的特性,又不用写SQL。顺便吐一下槽,官方说法是使用Core Data能减小50%-70%的代码量,但相信用过的人应该都内心明白,Core Data使用起来仍是比较麻烦的,这也是为何有很多的第三方类库来代替/二次包装Core Data。html
稍微复杂的应用就有可能出现同时处理多份数据的状况,这就须要用到多线程Core Data。在 iOS 5以前,官方推荐的是使用「Thread Confinement」,就是每一个线程使用独立的MOC(managed object context),而后共享一个PSC(persistent store coordinator)。同时在线程之间传递数据时,要传递objectID,而不是object,由于前者是线程安全的,后者不是。ios
若是A线程里,对PSC执行了CUD(create, update, delete)操做,其余线程如何感知呢?这就须要经过监听事件来实现。好比在线程A中监听「NSManagedObjectContextDidSaveNotification」事件,若是线程B中执行了CUD操做,线程A就能感知到,并触发响应的action,虽然能够经过noti userinfo来获取managed objects,但由于它们是关联到另外一个MOC,因此没法直接操做它们,解决方法就是调用「mergeChangesFromContextDidSaveNotification:」方法。git
用一张图来形容的话,大致就是这样:github
- (void)_setupCoreDataStack { // setup managed object model NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Database" withExtension:@"momd"]; _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; // setup persistent store coordinator NSURL *storeURL = [NSURL fileURLWithPath:[[NSString cachesPath] stringByAppendingPathComponent:@"Database.db"]]; NSError *error = nil; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_managedObjectModel]; if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { // handle error } // create MOC _managedObjectContext = [[NSManagedObjectContext alloc] init]; [_managedObjectContext setPersistentStoreCoordinator:_persistentStoreCoordinator]; // subscribe to change notifications [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil]; }
再来看看Notification Handler,主要做用就是合并新的变化。objective-c
- (void)_mocDidSaveNotification:(NSNotification *)notification { NSManagedObjectContext *savedContext = [notification object]; // ignore change notifications for the main MOC if (_managedObjectContext == savedContext) { return; } dispatch_sync(dispatch_get_main_queue(), ^{ [_managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; }); }
这种方式实现起来和维护起来都有点麻烦,因此iOS 5中就出现了更加方便和灵活的实现,也就是「Nested MOC」。数据库
[[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
能够看到在初始化时能够选择ConcurrencyType,可选的有3个:安全
这个是默认项,每一个线程一个独立的Context,主要是为了兼容以前的设计。多线程
建立一个private queue(使用GCD),这样就不会阻塞主线程。ide
建立一个main queue,使用主线程,会阻塞。spa
还有一个重要的变化是MOC能够指定parent。有了parent后,CUD操做会冒泡到parent。一个parent能够有多个child。parent还能够有parent。
由于UI相关的数据必须在主线程获取,同时又要避免数据库的I/O操做阻塞主线程,因此就有了下面这个模型:
我对这种实现方式的一个困惑是:child没法得知parent的变化,也就是说,若是NSFetchedResultsController绑定了Main MOC,当Background Write MOC save时,NSFetchedResultsController为什么能知晓?求指点。
这种方式比「Thread Confinement」稍微简单了点,也更明了。不过我的仍是推荐使用MagicalRecord,由于实现起来更加简单,等有空再写一篇。
写了一个使用了这个模型的demo,配合TableView和NSFetchedResultsController,有兴趣的能够看下:https://github.com/limboy/coredata-with-tableview
以前的困惑已消除,NSFetchedResultsController
跟PSC无关,只要绑定的MOC有了save
动做,NSFetchedResultsController
就会收到通知,不管这个save
操做有没有写入到持久层。