iOS核心数据在后台获取导致崩溃

Posted

技术标签:

【中文标题】iOS核心数据在后台获取导致崩溃【英文标题】:iOS Core Data fetch in background causing crashing 【发布时间】:2013-10-24 14:25:02 【问题描述】:

在我的应用程序中,我经常在后台执行 Core Data 工作。

Testflight 报告了很多关于从 Core Data 获取当前登录环境对象的特定提取的崩溃(我无法复制):

- (Environment *)getActiveEnvironment

    AppDelegate *ad = [AppDelegate sharedAppDelegate];
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
    [context setParentContext:ad.managedObjectContext];
    
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Environment" inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
    
    NSError *error;

    //crash points to this line
    NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
    
    Environment *tempE = nil;
    if(items.count > 0)
    
        for(Environment *e in items)
        
            if(!e.token && !e.url)
            
                break;
            
            
            Environment *mainContextTv = (Environment *)[ad.managedObjectContext objectWithID:e.objectID];
            if(e.activeValue)
                 tempE = mainContextTv;                
          
    

    return tempE;

这是我从 Testflight 收到的崩溃堆栈:

0   Tower-iSales-Tab    0x002b37b2  testflight_backtrace
1   Tower-iSales-Tab    0x002b2e4a  TFSignalHandler
2   libsystem_platform.dylib    0x39144722  _sigtramp
3   CoreData    0x2e1b8cec  _perform
4   CoreData    0x2e1c30f4  -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]
5   CoreData    0x2e13477a  -[NSManagedObjectContext executeFetchRequest:error:]
6   Tower-iSales-Tab    0x000c7082  -[MySingleton getActiveEnvironment] in MySingleton.m on Line 379
7   Tower-iSales-Tab    0x000c73f8  -[MySingleton getToken] in MySingleton.m on Line 416
8   Tower-iSales-Tab    0x0020c476  -[NetworkManager getPathForViewName:useTimeStamp:timeStamp:index:counter:] in NetworkManager.m on Line 699
9   Tower-iSales-Tab    0x0020aa5a  __70-[NetworkManager checkForDataFilesShouldEmptyQueue:isInitialDownload:]_block_invoke167 in NetworkManager.m on Line 415
10  libdispatch.dylib   0x3901b8fa  _dispatch_barrier_sync_f_invoke
11  Tower-iSales-Tab    0x0020a33e  __70-[NetworkManager checkForDataFilesShouldEmptyQueue:isInitialDownload:]_block_invoke113 in NetworkManager.m on Line 335
12  libdispatch.dylib   0x39017102  _dispatch_call_block_and_release
13  libdispatch.dylib   0x3901c7e4  _dispatch_root_queue_drain
14  libdispatch.dylib   0x3901c9d0  _dispatch_worker_thread2
15  libsystem_pthread.dylib 0x39146dfe  _pthread_wqthread
16  libsystem_pthread.dylib 0x39146cc3  start_wqthread

我读过this answer,它建议将队列类型设置为NSPrivateQueueConcurrencyType,而不是NSConfinementConcurrencyType,我可以这样做,但这不是我可以轻松复制的崩溃,所以我想确定会导致这次崩溃。

该答案还建议使用performBlockAndWait 块进行后台提取,但在我的函数中我该怎么做?我需要返回一个Environment 对象,我在整个应用程序中都使用了这个函数。

谢谢

【问题讨论】:

-getActiveEnvironment 是否在后台线程上运行? 我只是查看了我的调用,不,它并不总是在调度块中被调用。我认为这可能会导致一些问题。我相信当核心数据已经在后台同步时发生崩溃,我从主线程调用它?但我不确定 但它有时是否在后台线程上运行(例如从dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)调用? 实际上我收回了抱歉,我没有从任何调度块调用它。我存储当前环境有一个局部变量,然后那个在后台。但是,我需要在后台进行获取以避免主上下文中的任何锁定.. 如果这有意义.. 好的,所以你只从主线程调用-getActiveEnvironment,将结果存储为局部变量。你确定你以后不会从不同的线程中使用这个对象吗? 【参考方案1】:

据我了解,您不必在此处使用NSConfinementConcurrencyType 上下文,因为您仅从主线程调用-getActiveEnvironment。因此,您可以在主上下文 (ad.managedObjectContext) 上执行所有操作。

返回的对象应该只能从主线程访问。如果你想从其他线程访问它,你必须使用objectWithID: 在与该线程关联的上下文中获取它。

【讨论】:

我要试试这个,看看崩溃次数是否减少。我没有使用 ad.managedObjectContext 的原因是,如果应用程序正在同步,并且您按下一个调用 getActiveEnvironment 的按钮,它会在 UI 挂起时暂停后台同步一秒钟。不过,如果它可以防止崩溃,我可以忍受。谢谢,当我得到一些测试数据时会及时通知你 我现在从我的主线程调用这个函数(使用我的主存储上下文),它不会干扰后台处理。自从我进行更改后,我还没有看到崩溃出现。我也向任何遇到核心数据问题的人推荐这篇文章,它虽然旧但仍然有用:duckrowing.com/2010/03/11/using-core-data-on-multiple-threads【参考方案2】:

这个问题的原因很可能是您从与创建它的线程不同的线程访问NSManagedObjectContext..

黄金法则:始终从您创建 NSManagedObjectContext 的线程访问它。

因此,您需要确保您的 MOC 始终从同一个线程访问。

我相信NSPrivateQueueConcurrencyType 的东西可能是偶然相关的,这样做可能会解决问题(取决于您的代码究竟在做什么),但这可能不是这里的核心问题。

附注:持久对象存储没有相同的要求并且是线程安全的(通常您有多个 MOC 使用同一个持久对象存储)。

【讨论】:

谢谢。我最终为我的后台线程创建了一个单独的 NSPersistentStoreCoordinator,并且事情运行得更顺利了。 PSC 实际上是线程安全的,因此您不必这样做。参见例如***.com/questions/1976020/… 好吧,我观看了今年 WWDC 上关于 Core Data 的会议,他们建议在后台使用单独的 NSPersistentStoreCoordinator 来处理大数据——然后将主 NSPersistentStoreCoordinator 用于 UI。我适应了这一点,就像我说的那样,它似乎工作得更好。它也可以更好地工作,因为当我只使用 1 个 NSPersistentStoreCoordinator 时我做错了什么

以上是关于iOS核心数据在后台获取导致崩溃的主要内容,如果未能解决你的问题,请参考以下文章

核心数据多对多关系导致获取的结果控制器崩溃

核心数据导致 iPhone 崩溃

获取的结果导致部分崩溃

在后台获取核心数据更新的最佳方式

核心数据和多线程在保存时崩溃

仅在 iOS 14 中崩溃核心数据