自iOS 10以来发生的核心数据崩溃问题

3

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

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

  • 仅在iOS 10及以上版本中发生。
  • 最常在保存上下文时发生,但也可能在core data获取请求期间发生。
  • 相对较少发生(大约占会话的0.15%)
  • 已使用并发调试标志运行压力测试,以及一些xcode内存管理工具。未检测到任何问题。
  • 测试了内存泄漏。
  • 我从未能够在开发环境中重现此堆栈跟踪。
  • 在崩溃之前没有抛出任何异常。整个代码都被包装。
  • 此操作是在块内执行的,并且应用程序在前台运行。
  • 似乎在正常应用程序操作期间随机发生。(不是在初始化时间或任何特殊情况下)
  • 这是一个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


Core-data上下文不是线程安全的,无论是读取还是写入都不是。如果您违反了这个规则,core-data可能会在任何时候以任何方式失败。这意味着它可能会在代码中的正确线程上甚至在代码中难以诊断的崩溃报告点崩溃。我建议您检查代码是否存在线程安全性问题。首先,您可以查看将managedObjects作为参数传递的任何函数。 - Jon Rose
感谢您的输入。在测试应用程序时,我使用并发标志“-com.apple.CoreData.ConcurrencyDebug 1”,并且在最新版本中没有发现任何问题。我还手动检查了代码,因为我最初也怀疑存在并发问题。不幸的是,尽管如此,这个错误仍然在发生。 - Kieran
如果这种情况只发生在0.5%的构建中,那么这行代码(或者是特定的情况组合)可能非常罕见且难以通过代码测试找到。有时候,代码审查可以更好地发现这些问题。 - Jon Rose
@Kieran,你有解决这个问题的方法吗?我在iOS 10开始遇到了类似的问题。 - Julia
自iOS 10.3起,我们不再遇到这个问题。Google Analytics也出现了相同的崩溃 - 他们在iOS 10.3上也不再看到它。目前我仍不清楚根本原因。 - Kieran
2个回答

1
如果你的代码库大部分都是异步的,而你在这个异步块中尝试执行同步保存操作,那么很有理由怀疑这就是你收到错误消息中NSPersistentStoreCoordinator错误的原因。
关键问题在于NSPersistentStoreCoordinator(PSC)未能正确协调数据保存。除非我弄错了,否则错误消息标识当你要求PSC响应对该MOC的保存调用时,PSC被锁定。
在我看来,你的问题仍然最有可能源于对performBlock的调用......在这段代码中,你正在执行一个获取请求,然后更新一个属性,然后将对象插入回MOC,然后保存,所有这些都在同一个并发块中完成。这些是非常不同的功能,需要不同的处理能力和时间,都被倾泻到一个单一的并发块中。
此外,在使用并发和块时实例化属性的方式也很重要。你可能需要检查在你的代码中何时最适合实例化你的属性。
所以有一些问题...
  1. 您是否需要在performBlock中使用此代码的每一行?请注意,除非阻止了UI,否则获取请求和属性更新可能在调用performBlock之外的代码中是可以的。
  2. 如果您确实需要在诸如performBlock之类的core data并发块中使用此代码的每一行,请考虑将对save的调用嵌入到“block-within-a-block”中,并使用performBlockAndWait。

Apple developer website有一个示例,展示了将save调用嵌入到performBlockAndWait块中的方法,部分内容如下:

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 S. Zarra的“Core Data, 2nd Edition, Data Storage and Management for iOS, OS X, and iCloud”(2013年1月出版,来自The Pragmatic Bookshelf)中仍然得到了很好的解释,特别是第4章“性能调整”和第5章“线程”。
另一本关于核心数据的有价值的书籍是Apress出版社的“Pro iOS Persistence Using Core Data”,作者是Michael Privat和Robert Warner。

感谢您的回复。我应该提到,我上面引用的“context”对象是一个已经具有PrivateQueueConcurrencyType的私有托管对象上下文。大部分代码库都是独立的,并且与主队列异步无关。在这种情况下,我认为双重块不会真正为我们带来任何好处(尽管这是一个有趣的概念)。 - Kieran
我也发现(自从我昨天发布这篇文章以来)独立于核心数据的内存问题有类似的报告,见以下链接:https://forums.developer.apple.com/message/207825 和 https://forums.developer.apple.com/thread/63546我开始怀疑这是一个苹果iOS10内存管理问题。 - Kieran
我可能误解了你的情况,但我相信包含双重块会给你带来很多好处。请查看更新后的答案。 - andrewbuilder

0

iOS 10.3以后不再出现此问题。根本原因仍然未知。假设在10.2中存在某些iOS内存管理问题,而在10.3中得到了修复。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接