標籤:http os io 使用 ar for 資料 div cti
Core Data是iOS中很重要的一個部分,可以理解為基於SQLite(當然也可以是其他的Storage,如In-memory,只是SQLite比較常見)的一個ORM實現,所以有關聯式資料庫的特性,又不用寫SQL。順便吐一下槽,官方說法是使用Core Data能減少50%-70%的代碼量,但相信用過的人應該都心裡明白,Core Data使用起來還是比較麻煩的,這也是為什麼有不少的第三方類庫來代替/二次封裝Core Data。
稍微複雜的應用就有可能出現同時處理多份資料的情況,這就需要用到多線程Core Data。在 iOS 5之前,官方推薦的是使用「Thread Confinement」,就是每個線程使用獨立的MOC(managed object context),然後共用一個PSC(persistent store coordinator)。同時線上程之間傳遞資料時,要傳遞objectID,而不是object,因為前者是安全執行緒的,後者不是。
如果A線程裡,對PSC執行了CUD(create, update, delete)操作,其他線程如何感知呢?這就需要通過監聽事件來實現。比如線上程A中監聽「NSManagedObjectContextDidSaveNotification」事件,如果線程B中執行了CUD操作,線程A就能感知到,並觸發響應的action,雖然可以通過noti userinfo來擷取managed objects,但因為它們是關聯到另一個MOC,所以無法直接操作它們,解決方案就是調用「mergeChangesFromContextDidSaveNotification:」方法。
用一張圖來形容的話,大體就是這樣:
- (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,主要作用就是合并新的變化。
- (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個:
NSConfinementConcurrencyType
這個是預設項,每個線程一個獨立的Context,主要是為了相容之前的設計。
NSPrivateQueueConcurrencyType
建立一個private queue(使用GCD),這樣就不會阻塞主線程。
NSMainQueueConcurrencyType
建立一個main queue,使用主線程,會阻塞。
還有一個重要的變化是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
2013/06/17更新
之前的困惑已消除,NSFetchedResultsController跟PSC無關,只要綁定的MOC有了save動作,NSFetchedResultsController就會收到通知,無論這個save操作有沒有寫入到持久層。
zz 說說iOS的多線程Core Data