Why might releasing a managed object crash in -[_PFManagedObjectReferenceQueue _queueForDealloc:]?

前端 未结 4 1448
你的背包
你的背包 2020-12-31 19:37

I am occasionally seeing crashes with a stack trace like this:

0 libobjc.A.dylib 0x97dc0edb objc_msgSend + 27
1 com.apple.CoreData 0x97edcdc2 -[_PFManagedObj         


        
相关标签:
4条回答
  • 2020-12-31 19:39

    -_queueForDealloc: is an undocumented internal method. It shows up in stacks from time to time but it's nothing we deal with directly.

    Your problem is most likely caused by over-releasing a managedObject. A managedObject will be strongly retained by a context that inserts, updates or changes the object so if you micromanage the objects own retention, you can over-release it prior to the context releasing it. This cause the managed object to disappear at seemingly at random. Conversely, you can over-retain causing an object to persist after the context has deleted it.

    I encourage people to avoid retaining managed objects but when you do, put them a class property or a collection like an array or set. That way, the retention is handled for you.

    0 讨论(0)
  • 2020-12-31 19:40

    i has another solution so solve that bug. In examples, MOC properties for ARC looks like (readonly,strong,nonatomic)

    After weeks of dancing about that time-to-time crash i got solution for osx (just remove nonatomic).

    Now it perfect, all crashes go out.

    0 讨论(0)
  • 2020-12-31 19:48

    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 0x18f43c90
    0  libobjc.A.dylib                0x36f11f46 objc_msgSend + 5
    1  CoreData                       0x2928408f -[NSManagedObject release] + 166
    2  CoreData                       0x2927b4d7 -[_PFArray dealloc] + 94
    3  libobjc.A.dylib                0x36f201a9 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 404
    4  CoreFoundation                 0x294713a9 _CFAutoreleasePoolPop + 16
    5  Foundation                     0x2a1b6453 -[__NSOperationInternal _start:] + 1058
    6  Foundation                     0x2a25b44b __NSOQSchedule_f + 186
    7  libdispatch.dylib              0x3746d651 _dispatch_queue_drain + 952
    8  libdispatch.dylib              0x3746809d _dispatch_queue_invoke + 84
    9  libdispatch.dylib              0x3746eba1 _dispatch_root_queue_drain + 320
    10 libdispatch.dylib              0x3746fcd7 _dispatch_worker_thread3 + 94
    11 libsystem_pthread.dylib        0x375c6e31 _pthread_wqthread + 668
    
    
    Thread : Crashed: background queue :: NSOperation 0x1db59e80
    0  libsystem_kernel.dylib         0x3722edfc __pthread_kill + 8
    1  libsystem_pthread.dylib        0x372acd37 pthread_kill + 62
    2  libsystem_c.dylib              0x371ce909 abort + 76
    3  libsystem_malloc.dylib         0x37258331 szone_size
    4  libobjc.A.dylib                0x36bf1621 object_dispose + 20
    5  CoreData                       0x28ec571d -[_PFManagedObjectReferenceQueue dealloc] + 80
    6  CoreData                       0x28e5630f -[NSManagedObject dealloc] + 166
    7  CoreData                       0x28e55217 -[_PFManagedObjectReferenceQueue _queueForDealloc:] + 246
    8  CoreData                       0x28e5508f -[NSManagedObject release] + 166
    9  CoreData                       0x28e4c4d7 -[_PFArray dealloc] + 94
    10 libobjc.A.dylib                0x36c031a9 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 404
    11 CoreFoundation                 0x29042149 _CFAutoreleasePoolPop + 16
    12 Foundation                     0x29d88c23 -[__NSOperationInternal _start:] + 1058
    13 Foundation                     0x29e2dc1b __NSOQSchedule_f + 186
    14 libdispatch.dylib              0x371505b1 _dispatch_queue_drain + 952
    15 libdispatch.dylib              0x3714af85 _dispatch_queue_invoke + 84
    16 libdispatch.dylib              0x37151b9b _dispatch_root_queue_drain + 338
    17 libdispatch.dylib              0x37152cd7 _dispatch_worker_thread3 + 94
    18 libsystem_pthread.dylib        0x372a9e31 _pthread_wqthread + 668
    
    
    Thread : Crashed: NSOperationQueue Serial Queue
    0  libsystem_kernel.dylib         0x396871f0 __pthread_kill + 8
    1  libsystem_pthread.dylib        0x396ef7b7 pthread_kill + 58
    2  libsystem_c.dylib              0x39637ff9 abort + 76
    3  libsystem_malloc.dylib         0x396aed25 szone_size
    4  libobjc.A.dylib                0x390d93a9 object_dispose + 20
    5  CoreData                       0x2e3d4081 -[_PFManagedObjectReferenceQueue dealloc] + 80
    6  CoreData                       0x2e3655b7 -[NSManagedObject dealloc] + 166
    7  CoreData                       0x2e364501 -[_PFManagedObjectReferenceQueue _queueForDealloc:] + 244
    8  CoreData                       0x2e36437d -[NSManagedObject release] + 164
    9  CoreData                       0x2e35b867 -[_PFArray dealloc] + 94
    10 libobjc.A.dylib                0x390e20d3 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 358
    11 CoreFoundation                 0x2e5294c1 _CFAutoreleasePoolPop + 16
    12 Foundation                     0x2ef29999 -[__NSOperationInternal _start:] + 1064
    13 Foundation                     0x2efcd745 __NSOQSchedule_f + 60
    14 libdispatch.dylib              0x395c0cbd _dispatch_queue_drain + 488
    15 libdispatch.dylib              0x395bdc6f _dispatch_queue_invoke + 42
    16 libdispatch.dylib              0x395c15f1 _dispatch_root_queue_drain + 76
    17 libdispatch.dylib              0x395c18dd _dispatch_worker_thread2 + 56
    18 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://aceontech.com/objc/ios/2014/01/10/weakify-a-more-elegant-solution-to-weakself.html

    0 讨论(0)
  • 2020-12-31 19:51

    Having finally been able to reproduce the problem on a development machine, it seems this crash is a side-effect of an earlier exception during context teardown.

    The sequence of events is something like:

    1. The MOC is being deallocated, so it's time to tear down its contents
    2. To do so, all registered MOs are turned into faults*
    3. The act of turning a MO into a fault sends KVO-notifications
    4. An observer receives the notification and tries to act upon it, hitting a now invalid MO in the graph
    5. Core Data throws an exception from the invalid access
    6. For reasons unknown, that exception is not passed to my exception reporter
    7. The MOs get released, but the exception left Core Data in an unexpected state, so the MO deallocation crashes

    In short the real problem is that observers outlive the context; don't allow them to! Any object observing a MO should probably also have a strong reference to the MOC, like NSObjectController and friends do.

    *I found in testing that Core Data often does this on a background thread, presumably to avoid blocking the main thread

    MOC – managed object context
    MO – managed object

    0 讨论(0)
提交回复
热议问题