2014-11-05 45 views
0

我正在使用「老派」核心數據併發模型。換句話說,我有一個主要NSManagedObjectContext,它是用NSMainQueueConcurrencyType創建的,任何網絡請求都會在新創建的NSPrivateQueueConcurrencyType上下文中處理JSON響應數據。上下文共享persistentStoreCoordinator而不是使用parentContext,並且手動完成上下文合併。在iOS 8中合併核心數據NSManagedObjectContexts

由於iOS 8,一些我的設備展示了似乎上下文合併無法正常工作的行爲。在觀察NSManagedObjectContextDidSaveNotification後,我通過在我的主要上下文中調用mergeChangesFromContextDidSaveNotification將合併更改合併到我的主要上下文中。之後,我嘗試使用objectWithID:獲取活動對象圖的主要上下文版本,該表格似乎可以正常工作。但是,仔細觀察返回的對象後,任何NSSet關係都是空的,即使第二上下文版本具有它們。*

奇怪的是,相同的代碼在運行iOS 8的iPhone 6/6 +上產生準確的上下文合併。我的iOS 7 iPod Touch 5G可以正常工作。始終失敗的設備是相同的iOS 8 iPod Touch 5G。它也適用於所有模擬器。

是否有其他人看到過類似的行爲或對於可能導致此設備特定的核心數據問題的看法?提前致謝。

__block id toReturn = [self processResponse:responseData]; //Complete object graph from api. 

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
[context setPersistentStoreCoordinator:[self persistentStoreCoordinator]]; //shared PSC 
[context setUndoManager:nil]; 
[context setStalenessInterval:0]; 
[context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy]; 

NSManagedObjectContext *mainContext = [self mainContext]; 
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification 
                object:context 
                queue:[NSOperationQueue mainQueue] 
               usingBlock: 
      ^(NSNotification *notification) { 
       [mainContext mergeChangesFromContextDidSaveNotification:notification]; 
       NSManagedObjectID *managedId = [toReturn objectID]; 
       NSManagedObject *mainContextVersion = [mainContext objectWithID:managedId]; 
       toReturn = mainContextVersion; //*This is where the object graph is incomplete 
      }]; 

//Save context, will fire NSManagedObjectContextDidSaveNotification. 
NSError *errorWhileSaving; 
[context save:&errorWhileSaving]; 
+0

你的第一段概括您的問題很好。它不是特定於設備的,它與使用隊列限制和合並通知有關 - 它們不能很好地協同工作,並且如果使用performBlockAndWait,由於缺少用戶事件處理,您將很難診斷問題。 – quellish 2014-11-05 19:36:05

+0

我沒有使用'performBlockAndWait.'你的意思是「沒有使用」@quellish?如果是這樣的話,我只是覺得我只需要使用'performBlock'或'performBlockAndWait',因爲'save'是一個同步事件,並且通知僅在保存完成後觸發。在任何情況下,我都嘗試在'performBlockAndWait'塊中包裝'save'和'mergeChanges ...'調用,結果沒有變化。 – Keller 2014-11-05 20:45:44

+1

我的意思是「是」。如果您正在使用除NSConfinementConcurrencyType之外的任何內容創建的上下文,則除了上下文自己的存取方法外,您必須使用塊方法來訪問上下文的任何部分,包括對象及其屬性。 – quellish 2014-11-05 21:31:45

回答

1

我不會玩這個遊戲你似乎與toReturn它看起來像它開始作爲私人上下文中的管理對象,併成爲主要的背景下,管理對象打。

您應該使用託管對象ID作爲全局塊變量(或者,更好的是,根本沒有全局變量),因此您不必窺探託管對象即可獲取它。我見過很多聲明,您可以在不使用performBlock的情況下獲得ObjectID,但我從來不會碰到performBlock之外的任何MO/MOC。決不。永遠。過去太多頭痛,所以我只是堅持我的槍。

__block NSManagedObjectID *toReturnObjectID; 

但是,如果你想這樣做,你必須首先從正確的上下文訪問它。乍一看,這樣的事情......這是非常醜陋的,但說明了這一點......

[mainContext mergeChangesFromContextDidSaveNotification:notification]; 
[toReturn.context performBlock: ^{ 
    NSManagedObjectID *managedId = [toReturn objectID]; 
    [mainContext performBlock: ^{ 
     NSManagedObject *mainContextVersion = [mainContext objectWithID:managedId]; 
     toReturn = mainContextVersion; //*This is where the object graph is incomplete 
     // All the rest of the code that needs to happen... 

另請注意,您的全球toReturn的任何訪問調用保存在其他情況下可能會帶來麻煩之後。基本上,你不應該那樣做。處理完成後,您已經發出通知。爲什麼不將toReturn放入通知用戶信息中,而不是將它堆疊到全局變量中?此外,您可以將ObjectID放入發佈did-save通知的上下文的userInfo中,並在觸發通知時將其從該處取出。

如果讓通知在其自己的線程中運行(而不是讓該塊運行的隊列),則可以訪問保存的MOC,因爲您正在其活動線程/隊列/上下文中運行。保存前將對象ID移入userInfo。

喜歡的東西...

[[NSNotificationCenter defaultCenter] 
    addObserverForName:NSManagedObjectContextDidSaveNotification 
       object:context 
       queue:nil 
      usingBlock:^(NSNotification *notification) { 
     // we can access the context here, since we are running in its notification 
     NSManagedObjectContext *context = [notification object]; 
     NSManagedObjectID *managedId = [[context userInfo] objectForKey:@"ObjectGraphOID"]; 
     // Now that we have the object ID, capture it in the block that will do the work... 
     [mainContext performBlock:^{ 
      [mainContext mergeChangesFromContextDidSaveNotification:notification]; 
      NSManagedObject *mainContextVersion = [mainContext objectWithID:managedId]; 

      NSMutableDictionary *mainContextUserInfo = 
       [[self migratedInsertedObjectsDictionaryFromUserInfo:[notification userInfo]] mutableCopy]; 

      // Pass the result as part of userInfo of the notification 
      mainContextUserInfo[@"ObjectGraph"] = mainContextVersion; 
      [[NSNotificationCenter defaultCenter] 
       postNotificationName:FPFlatpackCycleCompleted 
           object:self 
          userInfo:mainContextUserInfo]; 
     }]; 
    }];