从异步 Web 服务响应更新托管对象的最佳方法?

Posted

技术标签:

【中文标题】从异步 Web 服务响应更新托管对象的最佳方法?【英文标题】:Best approach to update managed objects from async web services responses? 【发布时间】:2015-09-27 07:50:12 【问题描述】:

我有一个与主线程 (mainContext) 关联的 NSManagedObjectContext,我在其中获取我在整个应用程序中显示的所有 NSManagedObject

用户不编辑这些对象,但我从 Web 服务获取更新。我定期对此类服务执行异步调用,它们“告诉”我必须删除哪些托管对象(如果有),哪些必须用新信息更新(如果有),以及是否需要插入新对象。

所以,我需要首先获取所有服务的响应,然后检查我必须对我的mainContext 中已有的托管对象进行哪些更改。而且我还需要执行更新以避免阻塞 UI。

我正在考虑两种方法来管理这种情况:

    在具有自己的核心数据堆栈的私有队列中使用完全分离的privateContext 来插入我从服务中获得的所有对象。然后以某种方式(如何?)与我在mainContext 中的对象进行比较,并删除/修改/插入mainContext 中的对象。 在专用队列中使用privateContext,但作为mainContext 的子级。然后我需要将我在其父 mainContext 中的对象传递给子上下文(这可能吗?如何?),同时在这个子上下文中插入我从服务中获得的对象,然后比较和执行更改。

哪种方法是最好的或合适的?或者也许它应该是一个我没有想过的不同的?

提前致谢

编辑:这可能是另一种可能的方式吗?:

    仅使用 mainContext 并且,当我解析服务的响应时,而不是创建新对象,只需对 mainContext 进行一一更改...

编辑 2: 另一种可能性?:

    仅使用privateContext,获取服务响应并创建新对象。然后,还用这个privateContext 获取所有已经存在的对象(这将与mainContext 中的对象相同)。在此 privateContext 中进行更改,比较两组对象(最近从服务创建的对象和获取的对象),保存此上下文,清除 mainContext 并重新获取 mainContext 中的所有对象。

【问题讨论】:

你的对象没有任何 ID 属性吗? @kirander 是的,他们有 将私有上下文与父上下文一起使用。通过从服务器响应中获取的 id 从存储中获取所需的对象。根据情况更新、删除或创建新的。 @kirander 那将是我刚刚在我的问题中编辑的选项 4?关键是,我在尝试将mainContext 设置为privateContext 的父级时遇到了错误,我不知道为什么......该选项不能通过使用单独的privateContext 来工作吗? 这是 swift 还是 Obj-C? 【参考方案1】:

我不确定这是否是您的完整答案,但我正在处理类似的情况。我采取的实现路径是使用子对象(在所有地方) - 或者更像是临时子上下文的需要。

不过,我首先要提到的是确保使用 XCOde 中内置的 CoreData 调试功能。我做了第二个运行方案,它有

-com.apple.CoreData.ConcurrencyDebug 1

在应用程序初始化时,我有正常的NSManagedObjectContext - 它被传递到我的后台网络线程。

NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
   [child setParentContext:parentObjectContext];

然后,每当我需要将某些东西从父母传递给孩子时,我最终都会这样做:

[child objectWithID:(object-in-parent-context)]

或者我最终会这样做

__block AHRS_RPYL * ret;

[[self getChildContext] performBlockAndWait:^

    ret = [NSEntityDescription insertNewObjectForEntityForName:@"RPYL" inManagedObjectContext:[self getChildContext]];

// ... lots of code
];

我不能说我真的“喜欢”这种方法,我现在有点坚持它,但它似乎确实运作良好。

在我的大多数视图控制器之间我都有一个

@synthesize managedObjectContext;

在我的prepareForSegue 方法中

// Pass on managedObjectContext
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 

    //    dispatch_async(dispatch_get_main_queue(), ^
    // If the destination VC is able to take teh setManagedObjectContext method the current objectContext will be passed along.
    if ([segue.destinationViewController respondsToSelector:@selector(setManagedObjectContext:)]) 
        [segue.destinationViewController performSelector:@selector(setManagedObjectContext:)
                                              withObject:self.managedObjectContext];

     else 

        NSLog(@"Segue to controller [%@] that does not support passing managedObjectContext", [segue destinationViewController]);
    
    //    );

总结

我已经使用了您的第二选择选项,它是可行的,尽管在我诚实的意见中感觉有点笨拙。事实上,我们正在考虑切换到 Realm 而不是 CoreData,因为无论您怎么看,CoreData 都不是对线程最友好的选项。

UDPATES

保存代码

我实际上每 50 条消息保存到子上下文,每 250 条消息保存到父上下文(好久没碰过这段代码了)。我不能保证这是做事的最佳正确方法,但它确实有效,并且确实将磁盘访问降至最低。我收到很多消息,比如每秒 20 多条,所以我想做这种堆栈类型。你可能不在乎

(self.getChild()) 返回子上下文 - 我留下的旧代码。

if (msgCount % 50 == 0) 
                // Child Save!

                //                    NSLog(@"Saving SDatas");
                __block NSManagedObjectContext *currentChild = [self getChildContext];
                [self incChildContext];

                // Parent-Child save methodology
                [currentChild performBlock:^
                    NSError *error;
                    if (![currentChild save:&error]) 
                        NSLog(@"\n error => %@ \n", [error localizedDescription]);
                        NSLog(@" error => %@ ", [error userInfo]);
                        [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];

                        //                           abort();
                    

                    if (msgCount % 250 < 5) 

                        [parentObjectContext performBlock:^
                            NSError *error;
                            if (![parentObjectContext save:&error]) 
                                NSLog(@"\n error => %@ \n", [error localizedDescription]);
                                NSLog(@" error => %@ ", [error userInfo]);
                                [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];

                                //                                   abort();


                            
                        ];
                    

                    [currentChild reset];
                ];


            

删除

 [childContext performBlock:^

 // LOTS OF CODE
 // to build a query set of the records we want to kill
// End lots of code
[childContext deleteObject:msg];


        if (i % modFactor == 0) 
            self.percentDone = i / totalRecords;

            NSLog(@"%.1f Saving ...", self.percentDone * 100);


            NSError *error;
            if (![childContext save:&error]) 
                NSLog(@"\n error => %@ \n", [error localizedDescription]);
                NSLog(@" error => %@ ", [error userInfo]);
                [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];

                //                    abort();
            

            [parentContext performBlock:^
                NSError *errrror;
                if (![parentContext save:&errrror]) 
                    NSLog(@"\n error => %@ \n", [error localizedDescription]);
                    NSLog(@" error => %@ ", [error userInfo]);
                    [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];

                    //                        abort();
                
            ];
        

];  

...

再次 - 就像我之前说的,这可能不是最好的做事方式,但我确实有一个父/子堆栈,它确实有效。这是我编写的第一个 ios 应用程序之一,如果要重做,我会使用核心数据并使用其他东西。

我们的更新率非常高,所以当我们使用单个堆栈时,它会使 UI 变得非常慢。向父母/孩子的迁移很慢 - 如果我再做一次,我认为它会变得更顺利,因为我会编写许多辅助函数来处理其中的一些(或者只是使用 swift)。

祝你好运。

【讨论】:

感谢您的解释。我猜应该在子上下文的performBlockAndWait: 中调用[child objectWithID:(object-in-parent-context)],对吧? 关于这种方法:当您从子上下文中删除从其父级传递的对象时,当子级保存时它也从父级中删除?在子上下文中插入的相同问题。我需要在执行更新时进行删除和插入,而不仅仅是现有对象的更改......再次感谢

以上是关于从异步 Web 服务响应更新托管对象的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

基于异步响应轮询 Oracle 表的最佳方法

收到异步 POST 响应,需要更新网页

从 .NET 控制台调用 JSON WebService 的最佳方式

转 Web程序优化的最佳实践(网站内容篇)

Camel Restlet异步发送对客户端的响应

将分页信息传递给RestController的最佳方法是什么?