iOS - MagicalRecord/AFNetworking/NSFetchedResultsController - 后台重新同步过程导致永久挂起

Rob*_*hen 6 core-data nsfetchedresultscontroller ios afnetworking magicalrecord

所以我想要实现的目标

是一个应该在后台使用AFNetworking和Magical Record完成的同步过程,但当连接到NSFetchedResultsController的视图控制器当前打开或已打开(但弹出)时会导致永久挂起.

该应用程序在用户第一次打开手机时同步,然后通过Magical Record框架始终在Core Data持久性存储中使用数据.然后,当用户想要确保数据是最新版本时,他们进入设置并单击"重新同步",这将导致执行以下代码:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
    [[CoreDataOperator sharedOperator] commenceSync:self];
});
Run Code Online (Sandbox Code Playgroud)

这将使用单例CoreDataOperator(NSObject的子类 - 也许它应该是NSOperation?)启动同步过程,它会触发以下代码:

[[ApiClient sharedClient] getDataRequest];
Run Code Online (Sandbox Code Playgroud)

然后然后在单身AFHTTPClient子类中触发这个坏男孩:

[[ApiClient sharedClient] postPath:url parameters:dict
 success:^(AFHTTPRequestOperation *operation, id responseObject) {
 [request.sender performSelector:request.succeeded withObject:response];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
 [request.sender performSelector:request.failed withObject:response];
}
 ];
Run Code Online (Sandbox Code Playgroud)

有效地说:AFHTTPClient发布此信息,当它成功时,将信息传回给提供的选择器(我知道这是通用的,但请求不是问题)

现在,对A​​FNetworking进行编码,以便在主线程上调用所有完成选择器(在本例中具体为成功和失败); 所以为了防止阻塞主线程,处理响应并准备保存的代码被发送回后台线程:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    id responseData;
    [self clearAndSaveGetData:responseData];
});
Run Code Online (Sandbox Code Playgroud)

然后使用Magical Record框架调用导致保存的函数(仍在后台线程中):

NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread];

[DATAENTITY truncateAllInContext:localContext];
<!--- PROCESS DATA INTO DATAENTITY -->
[localContext saveNestedContexts];
Run Code Online (Sandbox Code Playgroud)

我选择saveNestedContexts是因为因为我在后台工作,所以我希望它一直推到默认上下文,我假设它是父上下文?(但到目前为止这还不是问题).

现在,这些数据可以成为成千上万行,因此我使用NSFetchedResultsController安全有效地访问这些数据,并且它们用于与设置或主页不同的视图控制器中.

以下是三种情况:

  1. 包含FRC的ViewController尚未被访问(不可见且以前未见过) - 后台同步工作完美无缺,减去了一点滞后,因为节省了堆栈.
  2. 包含FRC的ViewController已被访问并且当前可见 - 后台同步过程HANGS由于看起来似乎是FRC接收上下文更新.
  3. 包含FRC的ViewController以前是可访问的,但它当前不可见(使用以下代码弹出VC [self.navigationController popViewControllerAnimated:YES];:)并且在ViewDidUnload中将FRC设置为nil - 后台同步过程HANGS由于看起来像FRC接收上下文更新(就像在案例2中一样).

[PS:挂了几分钟后,我杀了调试器,它给了我一个SIGKILL以下代码,这就是为什么我在假设FRC正在接收导致它挂起的上下文更新:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
{
    UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView;
    [tableView endUpdates]; <---- SIGKILL
}
Run Code Online (Sandbox Code Playgroud)

其他重要信息:

  • 我使用2个独立的FRC,一个用于正常的ALL数据,另一个用于搜索
  • 我正在为FRC和单独的搜索FRC使用缓存,它在适当的时间被清除(在此上下文更新之前)
  • FRC正在主线程中获取(当存在数千行数据时会导致轻微挂起)并且我一直在寻找在后台获取,但是目前尚未实现.

问题:

  1. 为什么这种悬挂发生,VC可见或已经弹出?
  2. 我怎样才能使FRC不听取保存,但是在保存完成之前使用它所拥有的内容然后刷新数据(除非这已经发生并导致挂起)?
  3. 实现后台提取(因为使用FRC打开VC以访问数千行数据时的滞后会产生明显的滞后,即使缓存和减少的谓词/节标题 - 在1到4秒之间)是否可行?太复杂了?怎么做到呢?

谢谢,希望我在我的问题中足够详细.

Eng*_*epe 1

我知道这个问题很老了,但由于这是 MagicalRecord 的常见问题,也许以下内容会对某人有所帮助。

该问题是由fetchRequestFRC 和保存操作相互死锁引起的。以下为我解决了这个问题:

更新到最新的 MagicalRecord 版本(撰写本文时为 2.1)。

然后使用以下命令进行所有后台保存:

MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
  // create new objects, update existing objects. make sure you're only 
  // accessing objects inside localContext 
  [myObjectFromOutsideTheBlock MR_inContext:localContext]; //is your friend
}
completion:^(BOOL success, NSError *error) {
  // this gets called in the main queue. safe to update UI.
}];
Run Code Online (Sandbox Code Playgroud)