-->

合并来自父母和孩子之间的NSManagedObjectContext变化时NSManagedObje

2019-10-19 05:10发布

我同时使用2具有核心数据问题NSManagedObjectContext ,在不同的线程中运行,并迁移从父到子的变化。 从本质上说,我能够从父母拉出改变孩子,但这样做之后,更改将丢失。

我正在创建的应用程序是同步多个设备和服务器之间的模型进行测试。

保持所述用户与之交互的对象的上下文是主线程上,并且被配置为同步上下文的子,并且像这样创建的(错误检查省略)

NSManagedObjectContext *parentMOC = self.syncManagedObjectContext;
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

[_managedObjectContext performBlockAndWait:^() {
    [_managedObjectContext setParentContext:parentMOC];            
}];

该syncManagedObjectContext是父上下文和是其中syncManager执行与服务器同步。 它收集了由用户修改的对象,发送更改到服务器并接收合并变化回去。 所述syncManagedObjectContext也将其数据发送到PersistentStoreCoordinator要被存储在SQLite .The上下文中运行在后台“线程”以便同步和存储不阻塞主线程。 下面是它是如何创建的:

NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
_syncManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_syncManagedObjectContext performBlockAndWait:^(){
    [_syncManagedObjectContext setPersistentStoreCoordinator:coordinator];
}];

同步逻辑流程

当syncManager处理来自主上下文NSManagedObjectContextObjectsDidChangeNotification的同步将被断开。 这里是发生了什么粗糙的流程:

  1. syncManager处理NSManagedObjectContextObjectsDidChangeNotification可以让它知道对象已在主线程上下文被改变。 它节省话费上节省了变化syncMOC的主要方面。
  2. 当syncManager接收NSManagedObjectContextDidSaveNotification指示保存已经完成,它收集了从同步上下文中的新更改的对象,并发送更改服务器。 然后,它不保存上的同步MOC其将数据发送到的SQLite。 请注意,每个对象都有其创建便携式ID的UUID场 - 不与核心数据的ObjectID混淆,以及由服务器提供的lastSynced时间戳。
  3. 服务器与更新的时间戳发送的对象,以及已经发生的任何其他更改返回响应。 在说明这个问题最简单的情况下,接收到的是一个由UUID和更新lastSynced时间的syncManager刚刚发送的对象的一组记录。
  4. 对于每个更新时,更新syncManager在syncContext对象并存储NSManagedObject的objectID为在阵列中的对象(不是UUID)。
  5. 所述syncManager便无一在保存上的同步MOC将数据写入到磁盘和张贴消息以提供具有的objectID的的阵列,用于更新的对象的主要MOC。 在这一点上,如果我做一个获取在syncMOC所有实体,并将它们转储到日志中,它们都具有正确的价值观。 此外,如果我看在磁盘上的SQLite数据库,它也有正确的价值观。
  6. (注:我一直小心在代码中使用performBlock,看来这里的缩写代号为更新的方式合并在主线程上,用评论来发生了什么(一些错误检查和去除可有可无的玩意儿)在一切都在正确的线程发生的调试跟踪。)
-(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在程序中没有正确的价值观,即使我看到那个事情在上面的代码正确更新,并通过uniquing,他们也应该被更新。 我认为什么可能发生的是,我创建了正确值的“额外”的对象,而旧的仍然是各地。 然而,取数据表明,正确的对象,仅是正确的人身边,只有坏的价值观。

还有一两件事,如果我做的父上下文相同取这个函数运行后,它显示为不正确的值SQLite

任何帮助深表感谢!

Answer 1:

我终于找到了答案,这个问题,希望它可以帮助别人。

我注意到,在某些时候是回来的主上下文对象ID有不正确的核心数据ID - 他们应该是永久性的,但并非如此。 而事实上,在合并过程中,我意识到,我在主MOC一个给定的对象,并为改变该对象合并ID的ID都是暂时的,但不同的。 但是,无论是永久ID,他们应该是。 对于这个问题上堆栈溢出的搜索使我这个答案https://stackoverflow.com/a/11996957/1892468这给出了一个已知的核心数据错误解决方法。

所以,问题不是我做错了,这是核心数据并没有做什么,它说,它会做。 解决方法是,在主对象上下文保存操作中添加以下代码中调用保存之前。

if ctx.insertedObjects.count > 0 {
    do {
        try ctx.obtainPermanentIDsForObjects(Array(ctx.insertedObjects))
    } catch {
        log.error("Unable toobtain permanent ids for inserts")
    }
}

问题解决了! 那么,我原本一直在观察的是,合并并没有实际发生。 有2个对象活着为了什么应该是一个。



Answer 2:

你可以简单地订阅NSManagedObjectContextDidSaveNotification同步上下文,然后通过调用合并变为UI方面-mergeChangesFromContextDidSaveNotification:



文章来源: NSManagedObject values are correct, then incorrect when merging changes from parent to child NSManagedObjectContext