我正在使用"老派"核心数据并发模型。换句话说,我有一个使用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];
答案 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];
}];
}];