在iOS 8中合并核心数据NSManagedObjectContexts

时间:2014-11-05 17:37:40

标签: ios objective-c core-data

我正在使用"老派"核心数据并发模型。换句话说,我有一个使用NSManagedObjectContext创建的主NSMainQueueConcurrencyType,并且任何网络请求都会在新创建的NSPrivateQueueConcurrencyType上下文中处理JSON响应数据。上下文共享persistentStoreCoordinator而不是使用parentContext,并且上下文合并是手动完成的。

自从iOS 8以来,我的某些设备已经展示了一种似乎上下文合并无法正常工作的行为。在观察NSManagedObjectContextDidSaveNotification之后,我通过在我的主要上下文中调用mergeChangesFromContextDidSaveNotification将更改合并到我的主要上下文中。之后,我尝试使用objectWithID:获取活动对象图的主要上下文版本,这些版本在表面上看起来很有效。但是,在仔细检查返回的对象后,即使辅助上下文版本具有它们,任何NSSet关系都是空的。*

奇怪的是,相同的代码在运行iOS 8的iPhone 6/6 +上产生准确的上下文合并。甚至我的iOS 7 iPod Touch 5G也能正常工作。始终出现故障的设备是相同的iOS 8 iPod Touch 5G。它也适用于所有模拟器。

是否有其他人看到类似行为或了解可能导致此设备特定Core Data问题的原因?提前谢谢。

__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];

1 个答案:

答案 0 :(得分: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...

另请注意,在其他上下文中调用save之后对全局toReturn的任何访问都可能会造成麻烦。基本上,你不应该这样做。处理完成后,您已经发出通知。为什么不将toReturn放在通知用户信息中,而不是将其堆积成全局变量?此外,您可以将ObjectID放在发布保存通知的上下文的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];
        }];
    }];