objective-c - 自 iOS 10 以来发生的核心数据崩溃

标签 objective-c core-data ios10

自 iOS 10 以来,我一直在努力解决 sqlite/coredata 库中的本地无法重现的崩溃问题。它很少发生 - 在生产中大约为 0.2%。

我所知道的(或至少怀疑的):

  • 它只发生在 iOS 10 及更高版本上。
  • 最常发生在保存上下文时,但也可能发生在核心数据获取请求期间。
  • 很少发生(大概发生率为 0.15% 的 session )
  • 我在启用并发调试标志的情况下运行压力测试,以及一些 xcode 内存管理工具。未检测到任何问题。
  • 已测试内存泄漏。
  • 我从未能够在开发环境中重现此堆栈跟踪。
  • 在崩溃之前没有抛出任何异常。整个代码被包装。
  • 此操作在一个 block 内执行,应用程序在前台。
  • 在正常的应用程序操作过程中看似随机发生。 (不是在初始化时或任何特殊的时候)
  • 这是 SIGABRT 崩溃

libsystem_kernel.dylib0x00000001841c3014 __pthread_kill+4 libsystem_c.dylib0x0000000184137400 abort+136 libsystem_malloc.dylib0x0000000184207a5c nanozone_error+328 libsystem_malloc.dylib0x0000000184209028 nano_realloc+644 libsystem_malloc.dylib0x00000001841fb240 malloc_zone_realloc+176 libsqlite3.dylib0x0000000185730c34 sqlite3_value_text+1220 libsqlite3.dylib0x0000000185777f38 sqlite3_rekey+1564 libsqlite3.dylib0x000000018578df78 sqlite3_rekey+91740 libsqlite3.dylib0x0000000185791c88 sqlite3_rekey+107372 libsqlite3.dylib0x000000018571df98 sqlite3_log+86448 libsqlite3.dylib0x0000000185757780 sqlite3_bind_int+11992 libsqlite3.dylib0x00000001856f1c80 sqlite3_exec+35188 libsqlite3.dylib0x00000001856eb608 sqlite3_exec+8956 libsqlite3.dylib0x00000001856ea838 sqlite3_exec+5420 libsqlite3.dylib0x00000001856e9f24 sqlite3_exec+3096 libsqlite3.dylib0x00000001856e9ae0 sqlite3_exec+2004 CoreData0x00000001874f1284 -[NSSQLiteConnectionprepareSQLStatement:]+468 CoreData0x00000001876166f0 -[NSSQLiteConnectionupdateRow:forRequestContext:]+496 CoreData0x00000001876c3430 _writeChangesForSaveRequest+1596 CoreData0x00000001876c4958 _executeSaveChangesRequest+312 CoreData0x00000001876ba7f4 -[NSSQLSaveChangesRequestContextexecuteRequestUsingConnection:]+40 CoreData0x00000001875cdaf8 __52-[NSSQLDefaultConnectionManagerhandleStoreRequest:]_block_invoke+256 libdispatch.dylib0x000000018407e1bc _dispatch_client_callout+12 libdispatch.dylib0x000000018408b7f0 _dispatch_barrier_sync_f_invoke+80 CoreData0x00000001875cd994 -[NSSQLDefaultConnectionManagerhandleStoreRequest:]+204 CoreData0x0000000187693f80 -[NSSQLCoreDispatchManagerrouteStoreRequest:]+284 CoreData0x00000001875fb7e4 -[NSSQLCoredispatchRequest:withRetries:]+196 CoreData0x00000001875f7560 -[NSSQLCoreprocessSaveChanges:forContext:]+200 CoreData0x00000001874f8360 -[NSSQLCoreexecuteRequest:withContext:error:]+744 CoreData0x00000001875da2f4 __65-[NSPersistentStoreCoordinatorexecuteRequest:withContext:error:]_block_invoke+3248 CoreData0x00000001875d2bf0 -[NSPersistentStoreCoordinator_routeHeavyweightBlock:]+272 CoreData0x00000001874f7f20 -[NSPersistentStoreCoordinatorexecuteRequest:withContext:error:]+404 CoreData0x00000001875195ac -[NSManagedObjectContextsave:]+2768

代码大致如下:

NSManagedObject *object = [[MyManagedObject alloc] init];

// This is actually within the init method
NSEntityDescription *desc = [NSEntityDescription entityForName:NSStringFromClass(object.class)
                                      inManagedObjectContext:context];

[object initWithEntity:desc insertIntoManagedObjectContext:nil];


// later on...
[context performBlock:^{

    // Fetch another (different) object from core data
    NSArray *fetchResults = [context executeFetchRequest:request error:&error];

    // Changing some properties of object with values from fetched results
    object.property = fetchResults[0].property;

    // insert the object
    [context insertObject:object];

    // save the context
    [context save:&error]
}

任何想法将不胜感激。

更新:

我发现此发行说明与 iOS 10.2 一致,这可能导致暴露了一些现有问题。目前尚不清楚更改是什么,或者它如何导致问题,但很可能这在某种程度上是相关的。

https://support.apple.com/en-us/HT207422 影响:处理恶意字符串可能会导致应用程序意外终止或任意代码执行 说明:处理字符串时存在内存损坏问题。此问题已通过改进边界检查得到解决。 CVE-2016-7663

最佳答案

如果按照您的建议,您的大部分代码库是异步的,并且您正尝试在此异步 block 中执行同步保存操作,那么有充分的理由怀疑这就是您收到 NSPersistentStoreCoordinator 的原因 错误信息中的错误。

关键是 NSPersistentStoreCoordinator (PSC) 未能正确协调数据保存的问题。除非我弄错了,否则当您要求 PSC 响应为该 MOC 保存的调用时,错误消息表明 PSC 已锁定。

以我的愚见,您的问题仍然很可能源于您对 performBlock 的调用...在这段代码中,您正在执行获取请求,然后更新属性,然后将对象插入回 MOC,然后保存,所有这些都在同一个 block 中。这些是非常不同的函数,需要不同数量的处理能力和时间,全部转储到一个并发 block 中。

此外,在使用并发和 block 时如何实例化属性也很重要。您可能需要检查代码中的哪个位置最适合实例化您的属性。

所以有些问题...

  1. 您是否需要将这段代码的每一行都放在一个 performBlock 中?请考虑,除非您阻止您的 UI,否则在 performBlock 调用之外的代码中获取请求和对您的属性的更新可能没问题。
  2. 如果您确实需要将此代码的每一行都放在核心数据并发 block 中,例如 performBlock,您是否考虑过将对 save 的调用嵌入到“ block - block 内”并使用 performBlockAndWait?

Apple developer website有一个将 save 调用嵌入到 performBlockAndWait block 中的示例,包括(部分)如下:

NSManagedObjectContext *moc = '…; //Our primary context on the main queue

NSManagedObjectContext *private = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[private setParentContext:moc];

[private performBlock:^{
    'Complete your fetch request and update the managed object's property.

    NSError *error = nil;
    if (![private save:&error]) {
        NSLog(@"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);
        abort();
    }
    [moc performBlockAndWait:^{
        NSError *error = nil;
        if (![moc save:&error]) {
            NSLog(@"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);
            abort();
        }
     }];
}];

如果您能够使用更多的代码和更详细的描述来更新您的问题,我可能能够为您的特定问题提供更准确的解决方案。

另外,我建议您做一些研究...

尽管这本书年代久远,但 Marcus 在“核心数据,第 2 版,适用于 iOS、OS X 和 iCloud 的数据存储和管理”(2013 年 1 月,来自 The Pragmatic Bookshelf)中仍然很好地解释了并发的概念S. Zarra,特别是标题为“性能调优”的第 4 章和标题为“线程”的第 5 章。

Apress 出版商的另一本关于核心数据的有值(value)的书 – “Pro iOS Persistence Using Core Data”,作者 Michael Privat 和 Robert Warner。

关于objective-c - 自 iOS 10 以来发生的核心数据崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42519574/

相关文章:

sorting - Core Data 后台排序

swift - 获取单个 Transformable 属性的请求 (Swift)

objective-c - 读取串口iOS

ios - 获取 ImageView 图片原始大小

ios - 如何像在 CNN 应用程序中那样在 UITableView 上扩展图像

objective-c - 检查正在使用的应用程序版本

cocoa - 将 NSSegmentedControl 与 CoreData 结合使用

swift - 如何在 iOS 10/Swift 3 中请求远程权限

swift - 使用 PHCachingImageManager().requestAVAsset 时崩溃

ios - QLPreviewController 编辑模式不起作用?