我在使用2 NSManagedObjectContext
时遇到核心数据问题,在不同的线程上运行,并将更改从父级迁移到子级。从本质上讲,我能够将更改从父级转移到子级,但在执行此操作后,更改将立即丢失。
我正在构建的应用程序是在多个设备和服务器之间同步模型的测试。
保存用户与之交互的对象的上下文位于主线程上,并配置为同步上下文的子级,并且像这样创建(省略错误检查)
NSManagedObjectContext *parentMOC = self.syncManagedObjectContext;
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext performBlockAndWait:^() {
[_managedObjectContext setParentContext:parentMOC];
}];
syncManagedObjectContext是父上下文,是syncManager与服务器同步的地方。它收集用户修改的对象,将更改发送到服务器并合并收到的更改。 syncManagedObjectContext还将其数据发送到PersistentStoreCoordinator
以存储在SQLite
中。上下文在后台“线程”上运行,以便同步和存储不会阻塞主线程。以下是它的创建方式:
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
_syncManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_syncManagedObjectContext performBlockAndWait:^(){
[_syncManagedObjectContext setPersistentStoreCoordinator:coordinator];
}];
当syncManager从主上下文处理NSManagedObjectContextObjectsDidChangeNotification时,将启动同步。以下是发生的事情的粗略流程:
NSManagedObjectContextObjectsDidChangeNotification
,让它知道主线程上下文中的对象已被更改。它调用save保存主上下文,将更改保存到syncMOC。NSManagedObjectContextDidSaveNotification
表示保存已完成时,它会从同步上下文中收集新更改的对象,并将更改发送到服务器。然后它在同步MOC上进行保存,将数据发送到SQLite。请注意,每个对象都有一个uuid字段,我将其创建为可移植ID - 不要与Core Data的objectID混淆,也不要与服务器提供的lastSynced时间戳混淆。-(void)syncUpdatedObjects: (NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
NSArray *updates = [userInfo objectForKey:@"updates"];
NSManagedObjectContext *ctx = self.managedObjectContext;
[ctx performBlock:^() {
NSError *error = nil;
for (NSManagedObjectID *objID in updates) {
NSManagedObject *o = [ctx existingObjectWithID:objID error:&error];
// if I print out o or inspect in the debugger, it has the correct, updated values.
if (o) {
[ctx refreshObject:o mergeChanges:YES];
// refreshObject causes o to be replaced by a fault, though the NSLog statement will pull it back.
// NOTE: I’ve used mergeChanges:NO and it doesn’t matter
NSLog(@"uuid=%@, lastSynced = %@", [o valueForKey:@"uuid”], [o valueForKey:@"lastSynced"]);
// Output: uuid=B689F28F-60DA-4E78-9841-1B932204C882, lastSynced = 2014-01-15 05:36:21 +0000
// This is correct. The object has been updated with the lastSynced value from the server.
}
}
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@“MyItem"
inManagedObjectContext:ctx];
request.entity = entity;
NSArray *results = [ctx executeFetchRequest:request error:&error];
for (MyItem *item in results)
NSLog(@"Item uuid %@ lastSynced %@ ", item.uuid, item.lastSynced);
// Output: uuid B689F28F-60DA-4E78-9841-1B932204C882 lastSynced 1970-01-01 00:00:00 +0000
// Now the objects have incorrect values!
}];
}
如果您错过了,NSLog
语句后的注释中存在问题。该对象最初具有来自父上下文的正确值,但随后它们变得不正确。具体来说,请查看时间戳。
有谁知道为什么会这样?我应该注意到,最后进行提取的业务是调试的一部分。我注意到在程序中保留的NSManagedObjects没有正确的值,即使我在上面的代码中看到事情已经正确更新并且通过单独使用,它们也应该更新。我认为可能发生的事情是我正在创建具有正确值的“额外”对象,而旧的对象仍然存在。然而,获取显示正确的对象和只有正确的对象,只有坏的值。
还有一件事,如果我在运行此函数后在父上下文中进行相同的提取,它会像SQLite
一样显示正确的值。
非常感谢任何帮助!
答案 0 :(得分:1)
我终于找到了这个问题的答案,希望它可以帮助别人。
我在某些时候注意到,返回主要上下文的对象ID不正确 核心数据ID - 它们应该是永久性的,但不是。事实上,在合并期间,我 意识到我的主MOC中给定对象的ID和要合并的更改的ID 那个对象既是暂时的,也是不同的。但它们本身不应该是永久性身份证。 针对该问题的Stack Overflow搜索引发了我的兴趣 这个答案https://stackoverflow.com/a/11996957/1892468为已知核心提供了一种解决方法 数据错误。
所以问题不在于我做错了,而是核心数据没有做到这一点 说它会的。解决方法是在主对象上下文的保存操作期间,我添加了以下内容 调用save之前的代码。
02
修好了!所以我最初观察的是合并实际上没有采取 地点。有两个对象活着,应该是一个。
答案 1 :(得分:0)
您只需订阅NSManagedObjectContextDidSaveNotification
同步上下文,然后通过调用-mergeChangesFromContextDidSaveNotification:
将更改合并到UI上下文中。