我正在使用GDCoreDataConcurrencyDebugging
而我并不完全理解我得到的错误。我使用AFNetworking
获取一些数据,并在成功块中将响应保存在Core Data中。
我可以使用以下代码获取错误:
NSURL *baseURL = ...
AFHTTPRequestOperationManager *operationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL];
[operationManager GET:@"layouts"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject){
// backgroundManagedObjectContext is of NSPrivateQueueConcurrencyType
NSManagedObjectContext *context = [AppDelegate backgroundManagedObjectContext];
[context performBlock:^{
// Fetch and modify some NSManagedObject
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Layout"];
Layout *layout = [context executeFetchRequest:request error:nil][0];
// Remove this and the error goes away
layout.name = @"foo";
// Or this, and the error goes away
[context save:nil];
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error){}];
来自GDCoreDataConcurrencyDebugging
的错误消息如下:
Invalid concurrent access to managed object calling 'release'; Stacktrace: (
0 Econ 0x000000010016fba3 ValidateConcurrency + 346
1 Econ 0x000000010016f85c CustomSubclassRelease + 18
2 CoreFoundation 0x00000001035718eb CFRelease + 603
3 CoreFoundation 0x0000000103580a82 __CFBasicHashDrain + 322
4 CoreFoundation 0x000000010357180c CFRelease + 380
5 CoreFoundation 0x00000001035a2c4d -[__NSDictionaryM dealloc] + 157
6 libobjc.A.dylib 0x0000000102ff128e _ZN11objc_object17sidetable_releaseEb + 236
7 Foundation 0x0000000101261e0b -[NSConcreteNotification dealloc] + 84
8 libobjc.A.dylib 0x0000000102ff128e _ZN11objc_object17sidetable_releaseEb + 236
9 Econ 0x0000000100022e8b __destroy_helper_block_426 + 59
10 libsystem_sim_blocks.dylib 0x000000010608a734 _Block_release + 196
11 CoreData 0x0000000100abbb61 developerSubmittedBlockToNSManagedObjectContextPerform + 433
12 libdispatch.dylib 0x0000000106010614 _dispatch_client_callout + 8
13 libdispatch.dylib 0x0000000105ff8a1c _dispatch_main_queue_callback_4CF + 1664
14 CoreFoundation 0x00000001036031f9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
15 CoreFoundation 0x00000001035c4dcb __CFRunLoopRun + 2043
16 CoreFoundation 0x00000001035c4366 CFRunLoopRunSpecific + 470
17 GraphicsServices 0x0000000105818a3e GSEventRunModal + 161
18 UIKit 0x0000000101cd1900 UIApplicationMain + 1282
19 Econ 0x00000001000a28ef main + 111
20 libdyld.dylib 0x0000000106044145 start + 1
21 ??? 0x0000000000000003 0x0 + 3
)
我错过了什么?
答案 0 :(得分:0)
看起来错误是由于在后台线程中访问托管对象Layout
而导致的。您的Layout
对象似乎在您访问它时已被释放。
看了docs,方法performBlock:
似乎有以下讨论:
如果使用 NSPrivateQueueConcurrencyType 或 NSMainQueueConcurrencyType 初始化上下文,则使用此方法向托管对象发送消息。此方法封装自动释放池和对processPendingChanges的调用。
我认为这里的关键是perfomBlock:
封装自动释放池。自动释放池旨在积极释放其中的所有对象,将其添加到并发情况中,您可能会遇到奇怪的行为。如果您注意到堆栈跟踪中系统调用_Block_release
,则可能是罪魁祸首。我并不完全确定,但我相信performBlock:
方法会对块进行排队,然后只要方便上下文(因此异步)就执行它。在您的块有机会运行之前,系统可能会释放关键资源。
我会尝试使用performBlockAndWait:
这是同步调用,并且docs中没有提及自动释放池。或者,使用标准dispatch_async(dispatch_get_main_queue(), ^{})
使用NSManagedObjectContext
在主线程上执行代码。或者忘记拨打performBlock:
。