【问题标题】:Crash in CFRelease when removing a range of objects from a mutable array从可变数组中删除一系列对象时 CFRelease 崩溃
【发布时间】:2012-04-10 21:32:40
【问题描述】:

我们的一位测试人员报告了以下崩溃:

0 APP_NAME_WAS_HERE 0x00074892 testflight_backtrace + 158
1 APP_NAME_WAS_HERE 0x000754bc TFSignalHandler + 244
2 libsystem_c.dylib 0x378ea7ec _sigtramp + 48
3 CoreFoundation 0x30ef42e6 CFRelease + 94
4 CoreFoundation 0x30f09a36 -[__NSArrayM removeObjectAtIndex:] + 294
5 CoreFoundation 0x30f4a65e -[NSMutableArray removeObjectsInRange:] + 90
6 APP_NAME_WAS_HERE 0x000570ca -[StoryViewController rewindToChunkIndex:] + 558
7 APP_NAME_WAS_HERE 0x00057396 -[StoryViewController restartChapter] + 22

很遗憾,我们无法重现崩溃 - 我们只能通过 TestFlight 获取崩溃日志。

我们确实收到了调试日志,以确认removeObjectsInRange 确实收到了正在执行的NSMutableArray 的有效范围。 (此外,这会引发异常而不是发出信号,对吧?)

我唯一的想法是该对象正在获得双重释放,但我不确定在 ARC 开启的情况下这是如何实现的?

请注意,被删除的对象是UIView 子类,并且在此之前,它们中的一些或全部可能已从其父视图中删除。所以如果他们在这个阶段被释放我不会感到惊讶,我只是不明白为什么会导致它崩溃!

编辑:为了验证它是一个过度释放的对象,我人为地尝试过度释放一个对象(在 ARC 环境中使用 CFRelease(__bridge (CFTypeRef) obj) 强制释放)以查看它会产生的崩溃日志。不幸的是,它有点不同,所以也许它毕竟不是过度发布?可能是某种涂鸦?

这是明确的过度发布的样子:

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x00000000, 0x00000000
Crashed Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libsystem_kernel.dylib          0x369c732c __pthread_kill + 8
1   libsystem_c.dylib               0x36c20208 pthread_kill + 48
2   libsystem_c.dylib               0x36c19298 abort + 88
3   libsystem_c.dylib               0x36bd437a free + 374
4   libobjc.A.dylib                 0x375e4d72 object_dispose + 14
5   CoreFoundation                  0x362e9618 -[NSObject dealloc] + 76
6   UIKit                           0x310323a8 -[UIView dealloc] + 620
7   libobjc.A.dylib                 0x375e416e _objc_rootRelease + 30
8   CoreFoundation                  0x362dc2e0 CFRelease + 88
9   APP_NAME_WAS_HERE                   0x000cea98 -[StoryViewController rewindToChunkIndex:] (StoryViewController.m:584)

这是一个过度发布的崩溃日志的样子:

【问题讨论】:

    标签: ios memory crash nsmutablearray


    【解决方案1】:

    如果你查看堆栈跟踪,崩溃的发生不是因为索引错误,而是因为对象的过度释放。

    NSArray 在添加对象时发送保留消息,在删除对象时发送释放消息。显然,该版本正在崩溃。

    这意味着,您过度释放了添加到数组中的对象。

    更新

    您的子视图是否被强烈拥有?您的所有权修饰符是“强”、“弱”还是 unsafe_unretained?即使在 ARC 中,如果您没有正确“拥有”您的变量,也可能会出现不平衡的保留调用。例如,由于您手动将视图添加和删除到另一个数组中,您应该“拥有”它。从 superview 中删除将向视图发送释放,而 addSubview 将发送保留。当您使用 XIB 构建视图时,XIB 加载机制使用 property'w 所有权修饰符,并在将其添加到视图 (StoryViewController.view) 时相应地增加保留计数。由于 XIB 加载机制将其添加到子视图中,因此您不应该卸载它。如果你想卸载它,你应该通过将你的子视图(插座)的属性类型更改为“强”来“拥有”它,否则,你最终会弄乱所有权。

    在编写 ARC 所有权修饰符时,开始考虑对象图以及谁拥有什么。 ARC 不像垃圾收集。这样的事情还是会发生:)

    【讨论】:

    • 这也是我的结论(正如我在原始帖子中提到的那样,虽然有点太简短了!)。所以我想真正的问题是:如何使用 ARC 双重释放对象?
    • 这很有趣。您可以在此处粘贴更多代码吗?你存储的对象是什么?是CF对象还是NSObject?
    • 这是一个UIViews 的数组,我刚刚从他们的超级视图中删除了,所以我通过从数组中删除来进行清理(我需要这样做,部分原因是它们的顺序很重要)。有太多相关代码要贴在这里,真的,因为视图是应用程序的核心!从理论上讲,引用计数怎么会在那个方向变得不平衡?也许是一些多线程问题?然而我的应用程序中几乎没有多线程......
    • 更新了我的答案。我不认为这是种族问题。
    • 对不起 Mugunth,我刚刚发现了问题并发布了我的答案。如您所见,这非常非常奇怪!感谢您的勇敢尝试 :-)
    【解决方案2】:

    我对该问题的解决方法是将编译器的优化级别从目标构建设置中的默认设置Fastest, Smallest [-Os] 降低到None [-O0](仅在发行版中设置)。

    我不确定这是否只是在回避问题,或者编译器中是否确实存在错误,但你去吧。它解释了为什么只有测试人员才能得到它。

    【讨论】:

    • 我认为您没有发现问题。您可以为 NSObject 创建一个类别并覆盖保留和释放以记录每个保留和释放。这样你就可以看到错误来自哪里。
    • 这只是运气,优化器绝对不会改变双重释放的对象。
    【解决方案3】:

    没有看到代码,很难说出真正的问题是什么。我敢打赌这是一个过度发布的东西。请记住,ARC 不适用于 Core Foundation 对象。

    您可能使用便捷构造函数而不是allocinit 分配了属性。此类对象是自动释放的,必须明确保留,否则它们将在下一个循环中立即释放。

    【讨论】:

      猜你喜欢
      • 2013-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多