为什么可能会释放托管对象 - [_ PFManagedObjectReferenceQueue _queueForDealloc:]?

时间:2011-03-08 14:58:11

标签: cocoa core-data nsmanagedobject

我偶尔会看到像这样的堆栈跟踪崩溃:

0 libobjc.A.dylib 0x97dc0edb objc_msgSend + 27
1 com.apple.CoreData 0x97edcdc2 -[_PFManagedObjectReferenceQueue _queueForDealloc:] + 162
2 com.apple.CoreData 0x97edccbe -[NSManagedObject release] + 94
3 com.apple.CoreFoundation 0x9318ef38 CFRelease + 152
4 com.apple.CoreFoundation 0x931a7460 __CFBasicHashStandardCallback + 384
5 com.apple.CoreFoundation 0x931a706e __CFBasicHashDrain + 478
6 com.apple.CoreFoundation 0x9318f101 _CFRelease + 353
7 com.apple.CoreFoundation 0x931bbc6d _CFAutoreleasePoolPop + 253
8 com.apple.Foundation 0x973270aa NSPopAutoreleasePool + 76
9 com.apple.Foundation 0x97326fd2 -[NSAutoreleasePool drain] + 130
10 com.apple.AppKit 0x95087185 -[NSApplication run] + 627
11 com.apple.AppKit 0x9507f2d9 NSApplicationMain + 574
12 com.karelia.Sandvox 0x70001ef6 start + 54

不幸的是,重现它是相当随机的。有没有人有什么想法会导致这样的崩溃?没有人在互联网上似乎没有提到-_queueForDealloc:之前没有帮助!

我对过去类似的问题有一个模糊的记忆,这是一个释放托管对象的症状,同时它还附加了KVO观察者。有人同意吗?

4 个答案:

答案 0 :(得分:11)

最终能够在开发机器上重现问题,看起来这次崩溃是上下文拆解时早期异常的副作用。

事件的顺序如下:

  1. MOC正在取消分配,所以是时候拆除其内容了
  2. 为此,所有已注册的MOs都会变成错误*
  3. MO变为故障的行为会发送KVO通知
  4. 观察员收到通知并尝试对其采取行动,在图表中点击现在无效的MO
  5. Core Data从无效访问中引发异常
  6. 由于原因未知,该异常未传递给我的例外记者
  7. MO被释放,但异常使Core Data处于意外状态,因此MO解除分配崩溃
  8. 简而言之,真正的问题是观察者比上下文更长久;不要让他们!观察MO的任何对象都应该对MOC 有强烈的引用,例如NSObjectController和朋友。

    *我在测试中发现Core Data经常在后台线程上执行此操作,可能是为了避免阻塞主线程

    MOC - 托管对象上下文
    MO - 托管对象

答案 1 :(得分:2)

-_queueForDealloc:是一种未记录的内部方法。它不时出现在堆栈中,但我们没有直接处理。

您的问题很可能是由于过度释放托管对象引起的。托管对象将由插入,更新或更改对象的上下文强烈保留,因此,如果您对对象自身保留进行微观管理,则可以在上下文释放之前对其进行过度释放。这导致被管理对象看似随意消失。相反,在上下文删除对象后,您可以过度保留导致对象持久化。

我鼓励人们避免保留托管对象,但是当你这样做时,将它们放在类属性或类似数组或集合的集合中。这样,保留就会为您处理。

答案 2 :(得分:1)

我们在NSOperation中使用私有托管对象上下文时遇到了类似的问题,我们最终通过削弱任何参数并使用私有@autoreleasepool来解决它。我将在下面进一步阐述。

我们当前的设置有一个NSOperationQueue,我们会在后台进行长时间的计算。该操作首先创建一个私有托管对象上下文,将父集设置为主对象上下文,然后去取其对象。

与此同时,我们在其他地方有一个单独的NSOperationQueue同步来自我们服务器的新数据,可能会添加,更新或删除我们的计算操作所使用的对象。

我们第一次看到一堆这些崩溃在野外,在本地重新调用它的唯一方法是让计算和同步操作连续运行,并在5-10分钟后,我们会看到类似于其中一个的崩溃下面:

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

我们多次审核了代码,但无法确定崩溃的原因。我们尝试启用NSZombies,但在我们获得repro之前很久就会耗尽内存。

我们最终做的是以下两件事:

<强> @autoreleasepool

位于我们[privateObjectContext performBlockAndWait:^{…}]内的NSOperationBlock内,我们将所有代码都包含在@autoreleasepool{…}中。这样,在该代码块期间检索的所有NSManagedObjects将在离开performBlockAndWait之前标记为发布。

<强> weakify / strongify

包含NSManagedObjects的任何参数在传递到块之前都会弱化,并且在块中强化一次。这样,因为我们不再对它们有强烈的引用,所以如果它们在等待NSOperation开始时变得过时,它们就会被释放。这是一篇关于弱化/强化如何运作的好文章:http://aceontech.com/objc/ios/2014/01/10/weakify-a-more-elegant-solution-to-weakself.html

答案 3 :(得分:0)

我有另一个解决方案,所以解决了这个bug。在示例中,ARC的MOC属性看起来像(只读,强,非原子)

经过几周的时间跳舞,我得到了osx的解决方案(只需删除非原子)。

现在它很完美,所有的崩溃都会消失。