iOS 崩溃 - 在当前参数寄存器中找到的选择器名称:保留

iOS crash - Selector name found in current argument registers: retain

在曲棍球应用仪表板中,我遇到崩溃并提示以下内容:

异常类型:SIGTRAP 异常代码:#0 在 0x1943f61e8 崩溃线程:7 应用程序特定信息: 在当前参数寄存器中找到的选择器名称:retain

异常类型:SIGSEGV 异常代码:SEGV_ACCERR 在 0x568855a90 崩溃线程:18 应用程序特定信息: objc_msgSend() 选择器名称:保留

两个崩溃组都遇到了相同的代码,但不知何故他们有不同的崩溃描述,我认为这应该是相同的根本原因。

这是我的代码的样子:

线程 1:

dispatch_queue_t queue;
@synchronized (self) {
    queue = _mySerialQueue;
}
dispatch_async(queue, ^{ // Crash happens here
    if (_ivar) {
        ...
    }
});

线程 2:

@synchronized (self) {
    _mySerialQueue = dispatch_queue_create(...);
}

这段代码可以运行成ARC问题吗?

前提是(请验证这些)

  • _mySerialQueue是实例变量
  • _mySerialQueue 仅在 @synchronized(self) 个块内访问
  • 您不通过任何 属性 或键值编码 ([theObject valueForKey:"mySerialQueue"])
  • 访问 _mySerialQueue
  • 您发布的所有代码都使用 ARC 运行

我只看到上述代码崩溃的原因之一。我仍然不是 100% 确定,但这是我的镜头:

当崩溃发生时,

self 正在被释放。如果在dealloc:

清理iVar可以检查
- (void)dealloc {
    @synchronized(self) {
        _mySerialQueue = nil;
    }

    // ...
}

这些崩溃极其罕见,例如至少百分之一。如果经常出现,我认为是其他地方的问题。

更改后,应用程序仍会崩溃,但 EXC_BAD_ACCESS 将位于较低的内存位置,因为您在调用 dispatch_async(nil, ...) 时取消引用 NULL 指针。崩溃位置很可能是 0x00000050,或者类似的低地址。

为什么我会这样想? 当一个对象被销毁时,iVars 在一个单独的通道中被名为 .cxx_destruct 的方法销毁。当发生这种情况时,iVar 可能处于被释放的过程中,但同时变量仍然指向该对象,因为它从未被清除。

重要提示:如果这有效,它只会用另一个罕见的崩溃替换一个罕见的崩溃,因为这意味着 self 某处存在竞争条件.


另一种可能的解决方案是尝试使用 thread sanitizer 重现该问题。在大多数情况下,它可以非常快速地跟踪此类问题。