自 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 时如何实例化属性也很重要。您可能需要检查代码中的哪个位置最适合实例化您的属性。
所以有些问题...
- 您是否需要将这段代码的每一行都放在一个 performBlock 中?请考虑,除非您阻止您的 UI,否则在
performBlock
调用之外的代码中获取请求和对您的属性的更新可能没问题。 - 如果您确实需要将此代码的每一行都放在核心数据并发 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/