核心数据。对托管对象调用的无效并发访问'发布'

时间:2015-04-12 11:46:51

标签: ios objective-c debugging core-data afnetworking

我正在使用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
)

我错过了什么?

1 个答案:

答案 0 :(得分:0)

看起来错误是由于在后台线程中访问托管对象Layout而导致的。您的Layout对象似乎在您访问它时已被释放。

看了docs,方法performBlock:似乎有以下讨论:

  

如果使用 NSPrivateQueueConcurrencyType NSMainQueueConcurrencyType 初始化上下文,则使用此方法向托管对象发送消息。此方法封装自动释放池和对processPendingChanges的调用。

我认为这里的关键是perfomBlock: 封装自动释放池。自动释放池旨在积极释放其中的所有对象,将其添加到并发情况中,您可能会遇到奇怪的行为。如果您注意到堆栈跟踪中系统调用_Block_release,则可能是罪魁祸首。我并不完全确定,但我相信performBlock:方法会对块进行排队,然后只要方便上下文(因此异步)就执行它。在您的块有机会运行之前,系统可能会释放关键资源。

我会尝试使用performBlockAndWait:这是同步调用,并且docs中没有提及自动释放池。或者,使用标准dispatch_async(dispatch_get_main_queue(), ^{})使用NSManagedObjectContext在主线程上执行代码。或者忘记拨打performBlock: