2014-11-05 44 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]; 
     }]; 
    }];