CoreData: Object being released in block "error: Mutating a managed object after it has been removed from its context"

Viewed 779

This code relies on the CoreData setup from an older forked version of Robbie Hanson's XMPPFramework.

I am retrofitting a data structure to be backed by a managed object CoreData and basically keep a reference to the NSManagedObjectID inside the data structure in question and then have accessors to get/set the data structure properties with the backing data in Core Data.

I am intermittently getting the following error (similar to):

CoreData: error: Mutating a managed object 0xd00000000014000a <x-coredata://90C02501-4756-44F0-ABA6-B725192772A6/ZZXMPPOneToOneInboxItemCoreDataObject/p5> (0x1742971b0) after it has been removed from its context.

I am getting the error both when setting and getting. Obviously the "getting" error is not a "mutating a managed object" error but the stack trace is the same with the release as a culprit.

Here is an example setter:

- (void)setMamUUID:(NSString *)mamUUID
{
    ZZXMPPInboxCoreDataStorage *storage = [ZZXMPPInboxCoreDataStorage sharedInstance];
    [storage executeBlock:^{
        ZZXMPPOneToOneInboxItemCoreDataObject *backingObject = (ZZXMPPOneToOneInboxItemCoreDataObject *)[storage inboxItemInsideBlockWithObjectID:self.itemCoreDataObjectID];

        backingObject.mamUUID = mamUUID;
    }];
}

and a getter:

- (NSString *)mamUUID
{
    ZZXMPPInboxCoreDataStorage *storage = [ZZXMPPInboxCoreDataStorage sharedInstance];
    __block NSString *_mamUUID;

    [storage executeBlock:^{
        ZZXMPPOneToOneInboxItemCoreDataObject *backingObject = (ZZXMPPOneToOneInboxItemCoreDataObject *)[storage inboxItemInsideBlockWithObjectID:self.itemCoreDataObjectID];

        _mamUUID = [backingObject mamUUID];
    }];

    return _mamUUID;
}

The inboxItemInsideBlockWithObjectID: is a helper routine meant to run inside an executeBlock: and looks like this

- (ZZXMPPInboxBaseMemberCoreDataObject *)inboxItemInsideBlockWithObjectID:(NSManagedObjectID *)objectID
{
    ZZXMPPInboxBaseMemberCoreDataObject *inboxItem = nil;
    inboxItem = [[self managedObjectContext] objectWithID:objectID];
    if (nil == inboxItem) {
        NSLog(@"..Unable to retrieve an Inbox Item with objectID %@.", objectID);
    }

    return inboxItem;
}

The "storage" object referenced in both is the XMPPCoreDataStorage object from the XMPPFramework and it's executeBlock: has this as the main code:

dispatch_sync(storageQueue, ^{ @autoreleasepool {

block();

// Since this is a synchronous request, we want to return as quickly as possible.
// So we delay the maybeSave operation til later.

dispatch_async(storageQueue, ^{ @autoreleasepool {

    [self maybeSave:OSAtomicDecrement32(&pendingRequests)];
}});

}});

The error is an objc_object::release() (backtrace to follow) and is showing in the debugger at the end of the block in the "setter" code above.

Here is the complete backtrace:

(lldb) bt
* thread #1, queue = 'ZZXMPPRoomHybrid', stop reason = EXC_BAD_ACCESS (code=1, address=0x3a8e9bec8)
    frame #0: 0x0000000180459704 libobjc.A.dylib`objc_object::release() + 8
  * frame #1: 0x000000010009c1f4 MyApp`__29-[ZZConversation setMamUUID:]_block_invoke((null)=0x000000016fda6838) at ZZConversation.m:280
    frame #2: 0x00000001003da11c MyApp`__36-[XMPPCoreDataStorage executeBlock:]_block_invoke((null)=0x000000016fda67b0) at XMPPCoreDataStorage.m:1063
    frame #3: 0x0000000103c55218 libdispatch.dylib`_dispatch_client_callout + 16
    frame #4: 0x0000000103c61dc8 libdispatch.dylib`_dispatch_barrier_sync_f_invoke + 156
    frame #5: 0x0000000103c65adc libdispatch.dylib`_dispatch_barrier_sync_f_slow + 452
    frame #6: 0x00000001003da098 MyApp`-[XMPPCoreDataStorage executeBlock:](self=0x00000001702e5100, _cmd="executeBlock:", block=0x000000010009c148) at XMPPCoreDataStorage.m:1061
    frame #7: 0x000000010009c0d8 MyApp`-[ZZConversation setMamUUID:](self=0x0000000174223d60, _cmd="setMamUUID:", mamUUID=@"50a35939-f48c-4011-8cfe-b5edf3641e20") at ZZConversation.m:276
    frame #8: 0x00000001000a3c80 MyApp`-[ZZConversation mamRequestWasSentWithUUID:](self=0x0000000174223d60, _cmd="mamRequestWasSentWithUUID:", uuid=@"50a35939-f48c-4011-8cfe-b5edf3641e20") at ZZConversation.m:886
    frame #9: 0x00000001000af894 MyApp`__58-[ZZXMPPMAMCoreDataStorage requestMessageArchiveWithUser:]_block_invoke((null)=0x00000001742567a0) at ZZXMPPMAMCoreDataStorage.m:73
    frame #10: 0x0000000103c55218 libdispatch.dylib`_dispatch_client_callout + 16
    frame #11: 0x0000000103c61334 libdispatch.dylib`_dispatch_continuation_pop + 708
    frame #12: 0x0000000103c6ff94 libdispatch.dylib`_dispatch_source_latch_and_call + 204
    frame #13: 0x0000000103c57300 libdispatch.dylib`_dispatch_source_invoke + 836
    frame #14: 0x0000000103c5a05c libdispatch.dylib`_dispatch_main_queue_callback_4CF + 652
    frame #15: 0x00000001819b6810 CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
    frame #16: 0x00000001819b43fc CoreFoundation`__CFRunLoopRun + 1660
    frame #17: 0x00000001818e22b8 CoreFoundation`CFRunLoopRunSpecific + 444
    frame #18: 0x0000000183396198 GraphicsServices`GSEventRunModal + 180
    frame #19: 0x00000001879297fc UIKit`-[UIApplication _run] + 684
    frame #20: 0x0000000187924534 UIKit`UIApplicationMain + 208
    frame #21: 0x000000010006a688 MyApp`main(argc=5, argv=0x000000016fda7a60) at main.m:16
    frame #22: 0x00000001808c55b8 libdyld.dylib`start + 4
(lldb) 

I am not sure why the managed object would be released mid-block. The @autoreleasepool surrounds the block and should not release anything while the "sync" block is running.

This happens intermittently and I can run the app several times before it will surface again. Usually, it appears on a "getter" and not a "setter", but since it happens on both and the code for both is similar I don't think that matters.

Why would this managed object instance be released at this point?

0 Answers
Related