自iOS 10起发生核心数据崩溃 [英] Core data crash occurring since iOS 10

查看:85
本文介绍了自iOS 10起发生核心数据崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

自从iOS 10在sqlite / coredata库中以来,我一直在努力解决本地无法复制的崩溃。它很少发生-在生产中处于0.2%的范围内。



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




  • 仅在iOS 10及更高版本上发生。

  • 大多数情况下会在保存上下文时发生,但也可能在获取核心数据时发生请求。

  • 发生的频率非常低(会话率为0.15%)

  • 我已经在压力测试中启用了并发调试标志,并且一些xcode内存管理工具。没有检测到问题。

  • 经过了内存泄漏测试。

  • 我从来没有能够在开发环境中重现此堆栈跟踪。

  • 崩溃前不会引发任何异常。整个代码都被包装。

  • 此操作在一个块内执行,而应用位于前台。

  • 似乎在正常情况下随机发生应用操作。 (不是在初始化时或特殊的时候)

  • 这是SIGABRT崩溃




libsystem_kernel.dylib0x00000001841c3014 __pthread_kill + 4
libsystem_c.dylib0x0000000184137400abort + 136
libsystem_malloc.dylib0x0000000184207a5c nanozone_error + 328
libsystem_malloc.dylib0x000000018b + b $ b_b libsystem_malloc.dylib0x000000018420b 240
libsqlite3.dylib0x0000000185730c34 sqlite3_value_text + 1220
libsqlite3.dylib0x0000000185777f38 sqlite3_rekey + 1564
libsqlite3.dylib0x000000018578df78 sqlite3_rekey + 91740
libsqlite3.dyre1x8688_3ddy1x8688_dyrex1
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
CoreData0x00000001874f166:pars_ [NSSQL $ ow] +496
CoreData0x00000001876c3430 _writeChangesForSaveRequest + 1596
CoreData0x00000001876c4958 _executeSaveChangesRequest + 312
CoreData0x00000001876ba7f4-[NSSQLSaveChangesRequestContextexecuteRequestUsingConnection:] + 40
CoreData0x00000001Controllercd:
CoreData0x00000001-cd: dylib0x000000018407e1bc _dispatch_client_callout + 12
libdispatch.dylib0x000000018408b7f0 _dispatch_barrier_sync_f_invoke + 80
CoreData0x00000001875cd994-[NSSQLDefaultConnectionManagerhandleStoreRequest:] + 204
CoreData0 7693f80-[NSSQLCoreDispatchManagerrouteStoreRequest:] + 284
CoreData0x00000001875fb7e4-[NSSQLCoredispatchRequest:withRetries:] + 196
CoreData0x00000001875f7560-[NSSQLCoreprocessSaveChanges:forContext:] + 200
CoreData0xNSRequest:SQL :: 196 +744
CoreData0x00000001875da2f4 __65- [NSPersistentStoreCoordinatorexecuteRequest:withContext:error:] _ block_invoke + 3248
CoreData0x00000001875d2bf0-[NSPersistentStoreCoordinator_routeHeavyweightBlock:] + 272
Persisordator:3 [$ Per $ CoCoordinatorexec:20] $ b CoreData0x00000001875195ac-[NSManagedObjectContextsave:] + 2768


代码通常如下所示:

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

//实际上是在init方法中
NSEntityDescription * desc = [NSEntityDescription entityForName:NSStringFromClass(object.class)
inManagedObjectContext:context];

[object initWithEntity:desc insertIntoManagedObjectContext:nil];


//稍后……
[context performBlock:^ {

//从核心数据$ b获取另一个(不同的)对象$ b NSArray * fetchResults = [上下文executeFetchRequest:request错误:& error];

//使用获取的结果中的值更改对象的某些属性
object.property = fetchResults [0] .property;

//插入对象
[context insertObject:object];

//保存上下文
[context save:& error]
}

任何想法将不胜感激。



更新:



我发现此发行说明与iOS 10.2吻合,这可能导致暴露了一些现有问题。尚不清楚更改是什么,或它可能如何导致问题,但似乎很可能是某种原因造成的。



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

解决方案

如果您建议的大部分代码库都是异步的,而您正在尝试在此异步块中执行同步保存操作,因此有充分理由怀疑这就是为什么您在错误消息中收到 NSPersistentStoreCoordinator 错误的原因。



关键是 NSPersistentStoreCoordinator (PSC)无法正确协调数据保存的问题。除非我没有记错,否则当您要求PSC响应该呼叫以保存该MOC时,错误消息将表明PSC已锁定。



在我的拙见中,您的问题仍然很可能源于您致电 performBlock 在此代码中,您正在执行获取请求,然后更新属性,然后将对象重新插入到MOC中,然后进行保存,所有操作均在同一块中。这些功能截然不同,占用了不同的处理能力和时间,所有功能都转储到一个并发块中。



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



所以有一些问题...


  1. 您需要每行吗performBlock中的代码?考虑到,除非您阻止UI,否则在 performBlock 调用之外的代码中,获取请求和对属性的更新可能就可以了。

  2. 如果确实需要此代码的每一行在核心数据并发块中,例如 performBlock ,您是否考虑过将调用嵌入到保存在块内并使用 performBlockAndWait

苹果开发人员网站的示例将 save 调用嵌入到 performBlockAndWait 块中(部分包含)以下:

  NSManagedObjectContext * moc ='…; //我们在主队列上的主要上下文

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

[private performBlock:^ {
’完成您的提取请求并更新托管对象的属性。

NSError *错误=无;
if(![private save:& error]){
NSLog(@错误保存上下文:%@ \n%@,[error localizedDescription],[error userInfo]);
abort();
}
[moc performBlockAndWait:^ {
NSError * error = nil;
if(![moc save:& error]){
NSLog(@错误保存上下文:%@ \n%@,[error localizedDescription],[error userInfo]);
abort();
}
}];
}];

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



我也建议您做一些研究...



尽管本书年代久远,并发的概念在Marcus S. Zarra的 Core Data,第二版,用于iOS,OS X和iCloud的数据存储和管理(2013年1月,来自The Pragmatic Bookshelf)中得到了很好的解释,特别是标题为第4章 《性能调优》和第5章的标题为线程。华纳。


I've been struggling to resolve a locally-unreproducible crash since iOS 10 within the sqlite/coredata library. It's occurring very infrequently - somewhere in the realm of 0.2% in production.

What I know (or at least suspect):

  • It only happens on iOS 10 and above.
  • Most often occurs while saving the context, but may also be occurring during a core data fetch request.
  • Occurring fairly rarely (ballpark rate of 0.15% of sessions)
  • I have run stress tests with concurrency debug flags enabled, as well as some of the xcode memory management tools. No issues detected.
  • Tested for memory leaks.
  • I've never been able to reproduce this stacktrace in a development environment.
  • No exceptions are being thrown prior to the crash. The entire code is wrapped.
  • This action is performed within a block, and the app is in the foreground.
  • Occurs seemingly at random during normal app operation. (Not at initialization time or anything special)
  • It's a SIGABRT crash

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

Here's what the code generally looks like:

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]
}

Any ideas would be greatly appreciated.

Update:

I found this release note that coincided with iOS 10.2, which may have caused some existing issue(s) to be exposed. It's not clear what the change was, or how it might cause problems, but it seems pretty likely that this is related somehow.

https://support.apple.com/en-us/HT207422 Impact: Processing malicious strings may lead to an unexpected application termination or arbitrary code execution Description: A memory corruption issue existed in the processing of strings. This issue was addressed through improved bounds checking. CVE-2016-7663

解决方案

If the majority of your code base is, as your suggest, asynchronous and you are trying to perform a synchronous save operation within this asynchronous block, there is every reason to suspect that is why you're receiving the NSPersistentStoreCoordinator error in the error message.

The key is the issue with the NSPersistentStoreCoordinator (PSC) failing to properly coordinate the data save. Unless I'm mistaken the error message identifies that the PSC is locked when you ask the PSC to respond to the call to save for that MOC.

In my humble opinion your problem still most likely stems from your call to performBlock... in this code you're performing a fetch request, then updating a property, then inserting the object back into the MOC, then saving, all in the same block. These are very different functions taking different amounts of processing power and time, all dumped into one single concurrency block.

Also, how you instantiate a property when using concurrency and blocks is important. You may need to check where in your code is most appropriate to instantiate your properties.

So some questions...

  1. Do you need every line of this code in a performBlock? Consider that, unless you're blocking your UI, the fetch request and the update to your property may be ok in code outside the call to performBlock.
  2. If you do need every line of this code in a core data concurrency block such as performBlock, have you considered instead embedding your call to save in a "block-within-a-block" and using performBlockAndWait?

The Apple developer website has an example of embedding a save call into a performBlockAndWait block, included (in part) following:

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();
        }
     }];
}];

If you're able to update your question with a little more code and a more detailed description I might be able to provide a more accurate fix for your specific problem.

Also I'd recommend you do some research...

Despite the age of the book, the concepts of concurrency are still very well explained in "Core Data, 2nd Edition, Data Storage and Management for iOS, OS X, and iCloud" (Jan 2013 from The Pragmatic Bookshelf) by Marcus S. Zarra, and in particular Chapter 4 titled "Performance Tuning" and Chapter 5 titled "Threading".

Another valuable book on core data from Apress publishers – "Pro iOS Persistence Using Core Data", by Michael Privat and Robert Warner.

这篇关于自iOS 10起发生核心数据崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆