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
操做有沒有寫入到持久層。