Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/112.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios NSFetchedResultsController在后台更新同一持久性存储时提供表视图会导致死锁_Ios_Objective C_Core Data_Magicalrecord - Fatal编程技术网

Ios NSFetchedResultsController在后台更新同一持久性存储时提供表视图会导致死锁

Ios NSFetchedResultsController在后台更新同一持久性存储时提供表视图会导致死锁,ios,objective-c,core-data,magicalrecord,Ios,Objective C,Core Data,Magicalrecord,仍在努力将应用程序从每次使用或显示信息时下载信息转换为使用CoreData在手机上缓存信息(MagicalRecord提供)。这是在iOS 7上 因为我们没有一个数据推送系统,在后端数据发生变化时自动更新手机的缓存数据,所以在过去的几个月里,我一直在思考(因为我们在处理应用程序的其他方面)如何管理在手机上保留数据的本地副本,并在缓存中保存最新数据 我意识到,只要我仍然每次提取数据:-(我可以使用手机的CoreData备份数据缓存来显示和使用数据,只需提取数据就可以更新手机数据库 所以我一直在将主

仍在努力将应用程序从每次使用或显示信息时下载信息转换为使用CoreData在手机上缓存信息(MagicalRecord提供)。这是在iOS 7上

因为我们没有一个数据推送系统,在后端数据发生变化时自动更新手机的缓存数据,所以在过去的几个月里,我一直在思考(因为我们在处理应用程序的其他方面)如何管理在手机上保留数据的本地副本,并在缓存中保存最新数据

我意识到,只要我仍然每次提取数据:-(我可以使用手机的CoreData备份数据缓存来显示和使用数据,只需提取数据就可以更新手机数据库

所以我一直在将主要数据对象从下载的数据转换成完整的对象,这些主要数据对象是CoreData对象的轻型替代对象

基本上,应用程序中的每个普通数据对象(而不是在内部包含对象的所有属性)只包含底层CoreData对象的对象,可能在内部包含特定于应用程序的ID,并且所有其他属性都是动态的,从CoreData对象获取并传递(大多数属性是只读的,更新是通过从传入的JSON中批量重写核心数据来完成的)

像这样:

- (NSString *)amount
{
    __block NSString *result = nil;

    NSManagedObjectContext *localContext = [NSManagedObjectContext MR_newContext];

    [localContext performBlockAndWait:^{
        FinTransaction  *transaction = (FinTransaction *)[localContext existingObjectWithID:[self objectID] error:nil];

        if (nil != transaction)
        {
            result = [transaction.amount stringValue];
        }
    }];

    return result;
}
有时需要设置一个,如下所示:

- (void)setStatus:(MyTransactionStatus)status
{
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
        FinTransaction *transaction = (FinTransaction *)[localContext existingObjectWithID:[self objectID] error:nil];

        if (nil != transaction)
        {
            transaction.statusValue = status;
        }

    } completion:^(BOOL success, NSError *error){}];
}
_frController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                    managedObjectContext:[NSManagedObjectContext MR_rootSavingContext]
                                                      sectionNameKeyPath:sectionKeyPath
                                                               cacheName:nil];
现在,我的问题是,我有一个视图控制器,它基本上使用NSFetchedResultsController在表视图中显示本地手机CoreData数据库中存储的数据用更新后的数据更新CoreData数据存储,然后在主线程上运行异步GCD回调,让获取的结果控制器重新读取其数据,并通知表视图重新加载

问题是,如果用户滚动初始获取结果控制器获取数据和表视图加载,并且后台线程在后台更新相同的核心数据对象,则会发生死锁。获取和重写的实体并不完全相同(发生死锁时),即,不是读取和写入对象ID 1,而是使用相同的持久数据存储

每次访问(读或写)都发生在
MR_saveWithBlock
MR_saveWithBlockAndWait
(数据写入/更新)中(视情况而定),以及[localContext performBlock:]或[localContext performBlockAndWait:]每个单独的读或写都有自己的
NSManagedObjectContext
。我没有看到任何地方有零散的挂起的更改,它阻塞和死锁的实际位置并不总是相同的,但总是与主线程从与后台threa相同的持久存储读取有关d用于更新数据

正在创建获取的结果控制器,如下所示:

- (void)setStatus:(MyTransactionStatus)status
{
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
        FinTransaction *transaction = (FinTransaction *)[localContext existingObjectWithID:[self objectID] error:nil];

        if (nil != transaction)
        {
            transaction.statusValue = status;
        }

    } completion:^(BOOL success, NSError *error){}];
}
_frController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                    managedObjectContext:[NSManagedObjectContext MR_rootSavingContext]
                                                      sectionNameKeyPath:sectionKeyPath
                                                               cacheName:nil];
然后执行
performFetch

在需要在表视图中显示数据块数据并在后台使用新数据更新数据存储的情况下,如何最好地构造此类操作


当我在大多数情况下使用
MagicalRecord
时,我愿意接受评论、答案等,无论有没有(纯CD)使用MagicalRecord。

因此,我处理这一问题的方法是查看两个托管对象上下文,每个上下文都有自己的持久存储协调器。两个持久存储协调器都与磁盘上的同一个持久存储进行通信

WWDC 2013年第211期“核心数据性能优化和调试”中详细介绍了这种方法,您可以在上一节中进行讨论


为了在MagicalRecord中使用这种方法,您需要考虑使用即将发布的MagicalRecord 3.0版本,以及
ClassicWithBackgroundCoordinatorSQLiteMagicalRecordStack
(是的,该名称需要修改!)。它实现了WWDC会话中概述的方法,但您需要知道,您的项目需要进行更改才能支持MagicalRecord 3,而且它还没有完全发布

基本上,你最终得到的是:

  • 1 x主线程上下文:您可以使用它来填充UI和获取的结果控制器等。永远不要在此上下文中进行更改
  • 1 x专用队列上下文:使用基于块的保存方法进行所有更改-它们会自动通过此上下文并保存到磁盘
我希望这是有意义的-一定要看WWDC会议-他们使用一些很棒的动画图来解释为什么这种方法更快(并且不应该像现在使用的方法那样阻塞主线程)


如果您需要的话,我很乐意提供更多的细节。

代码编辑实际上使imho更难阅读,但这没关系。我的代码格式化风格是基于80年代后期的研究而开发的,当时我在一本杂志上读到了这项研究,后来我通过使用对其进行了修改和扩展,并利用了我们人类阅读和处理信息的方式为处理80x24或80x25字符终端而开发的狭窄且难以阅读的样式,这些终端已流传多年……但我离题了。:)观看了视频。下载了当前正在工作的MR 3。正在处理此问题。谢谢!要使用
ClassicWithBackgroundCoordinatorSQLiteMagicalRecordStack
我假设设置为
SetupClassicStackWithQliteStoreName:
?是否有与
ClassicWithBackgroundCoordinatorSQLiteMagicalRecordS不同的ClassicStack