Crash with NSManagedObject release: "objc_msgSend() selector name: _queueForDealloc" Crash with NSManagedObject release: "objc_msgSend() selector name: _queueForDealloc" multithreading multithreading

Crash with NSManagedObject release: "objc_msgSend() selector name: _queueForDealloc"


We hit into a a similar issue when using a private managed object context inside an NSOperation and we ended up working around it by weakifying any parameters and using a private @autoreleasepool. I'll elaborate further below.

Our current set up has an NSOperationQueue which has a long running calculation we do in the background. The operation first creates a private managed object context with the parent set as the main object context and goes and fetches its objects.

In the mean time, we have a separate NSOperationQueue elsewhere that syncs down new data from our server, potentially adding, updating, or removing objects used by our calculation operation.

We first saw a bunch of these crashes out in the wild and the only way to repro it locally is to have both calculation and sync operations run continuously and after 5-10 minutes, we would see a crash similar to one of the below:

Thread : Crashed: background queue :: NSOperation 0x18f43c900  libobjc.A.dylib                0x36f11f46 objc_msgSend + 51  CoreData                       0x2928408f -[NSManagedObject release] + 1662  CoreData                       0x2927b4d7 -[_PFArray dealloc] + 943  libobjc.A.dylib                0x36f201a9 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 4044  CoreFoundation                 0x294713a9 _CFAutoreleasePoolPop + 165  Foundation                     0x2a1b6453 -[__NSOperationInternal _start:] + 10586  Foundation                     0x2a25b44b __NSOQSchedule_f + 1867  libdispatch.dylib              0x3746d651 _dispatch_queue_drain + 9528  libdispatch.dylib              0x3746809d _dispatch_queue_invoke + 849  libdispatch.dylib              0x3746eba1 _dispatch_root_queue_drain + 32010 libdispatch.dylib              0x3746fcd7 _dispatch_worker_thread3 + 9411 libsystem_pthread.dylib        0x375c6e31 _pthread_wqthread + 668Thread : Crashed: background queue :: NSOperation 0x1db59e800  libsystem_kernel.dylib         0x3722edfc __pthread_kill + 81  libsystem_pthread.dylib        0x372acd37 pthread_kill + 622  libsystem_c.dylib              0x371ce909 abort + 763  libsystem_malloc.dylib         0x37258331 szone_size4  libobjc.A.dylib                0x36bf1621 object_dispose + 205  CoreData                       0x28ec571d -[_PFManagedObjectReferenceQueue dealloc] + 806  CoreData                       0x28e5630f -[NSManagedObject dealloc] + 1667  CoreData                       0x28e55217 -[_PFManagedObjectReferenceQueue _queueForDealloc:] + 2468  CoreData                       0x28e5508f -[NSManagedObject release] + 1669  CoreData                       0x28e4c4d7 -[_PFArray dealloc] + 9410 libobjc.A.dylib                0x36c031a9 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 40411 CoreFoundation                 0x29042149 _CFAutoreleasePoolPop + 1612 Foundation                     0x29d88c23 -[__NSOperationInternal _start:] + 105813 Foundation                     0x29e2dc1b __NSOQSchedule_f + 18614 libdispatch.dylib              0x371505b1 _dispatch_queue_drain + 95215 libdispatch.dylib              0x3714af85 _dispatch_queue_invoke + 8416 libdispatch.dylib              0x37151b9b _dispatch_root_queue_drain + 33817 libdispatch.dylib              0x37152cd7 _dispatch_worker_thread3 + 9418 libsystem_pthread.dylib        0x372a9e31 _pthread_wqthread + 668Thread : Crashed: NSOperationQueue Serial Queue0  libsystem_kernel.dylib         0x396871f0 __pthread_kill + 81  libsystem_pthread.dylib        0x396ef7b7 pthread_kill + 582  libsystem_c.dylib              0x39637ff9 abort + 763  libsystem_malloc.dylib         0x396aed25 szone_size4  libobjc.A.dylib                0x390d93a9 object_dispose + 205  CoreData                       0x2e3d4081 -[_PFManagedObjectReferenceQueue dealloc] + 806  CoreData                       0x2e3655b7 -[NSManagedObject dealloc] + 1667  CoreData                       0x2e364501 -[_PFManagedObjectReferenceQueue _queueForDealloc:] + 2448  CoreData                       0x2e36437d -[NSManagedObject release] + 1649  CoreData                       0x2e35b867 -[_PFArray dealloc] + 9410 libobjc.A.dylib                0x390e20d3 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 35811 CoreFoundation                 0x2e5294c1 _CFAutoreleasePoolPop + 1612 Foundation                     0x2ef29999 -[__NSOperationInternal _start:] + 106413 Foundation                     0x2efcd745 __NSOQSchedule_f + 6014 libdispatch.dylib              0x395c0cbd _dispatch_queue_drain + 48815 libdispatch.dylib              0x395bdc6f _dispatch_queue_invoke + 4216 libdispatch.dylib              0x395c15f1 _dispatch_root_queue_drain + 7617 libdispatch.dylib              0x395c18dd _dispatch_worker_thread2 + 5618 libsystem_pthread.dylib        0x396ecc17 _pthread_wqthread + 298

We reviewed the code multiple times and was not able to determine why it was crashing. We tried enabling NSZombies, but would run out of memory long before we could get a repro.

What we ended up doing is the following 2 things:

@autoreleasepool

Inside our [privateObjectContext performBlockAndWait:^{…}] which resides inside our NSOperationBlock, we wrapped all the code inside an @autoreleasepool{…}. That way all NSManagedObjects retrieved during that block of code will be mark for release before leaving the performBlockAndWait.

weakify/strongify

Any parameters that include NSManagedObjects were weakify before passing it into the block, and strongify once in the block. This way since we no longer have a strong reference to them, they can be released if they become out of date while we wait for the NSOperation to start. Here's a good article on how weakify/strongify works: http://blog.aceontech.com/post/111694918560/weakifyself-a-more-elegant-solution-to