NotificationCenter 在模拟器上而不是在设备上导致 EXC_BAD_ACCESS 的任何原因?

Any reason why the NotificationCenter causes EXC_BAD_ACCESS on simulator, but not on device?

我这里有这段代码:

    @objc func buttonTapped(sender: DateFieldButton) {

        // Thread 1: EXC_BAD_ACCESS (code=1, address=0xfffe) here
        NotificationCenter.default.post(name: .dateFieldTapped, object: nil, userInfo: [0 : sender.pattern])

    }
extension Notification.Name {

    static let dateFieldTapped = Notification.Name("dateFieldTapped")

}

button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)

DeviceFieldButton只是UIButton的一个子类,多了一个pattern字段。在物理设备上(iPhone X on iOS 13.2),EXC_BAD_ACCESS 不会发生。它只能在模拟器中复制。 用相同的观察多次清理项目。

编辑:

回溯:

libobjc.A.dylib`objc_retain:
    0x7fff50b51c60 <+0>:  testq  %rdi, %rdi
    0x7fff50b51c63 <+3>:  je     0x7fff50b51c81            ; <+33>
    0x7fff50b51c65 <+5>:  js     0x7fff50b51c83            ; <+35>
->  0x7fff50b51c67 <+7>:  movq   (%rdi), %rax
    0x7fff50b51c6a <+10>: testb  [=13=]x4, 0x20(%rax)
    0x7fff50b51c6e <+14>: jne    0x7fff50b51c88            ; objc_object::sidetable_retain()
    0x7fff50b51c74 <+20>: movq   0x3908fee5(%rip), %rsi    ; "retain"
    0x7fff50b51c7b <+27>: jmpq   *0x36ced19f(%rip)         ; (void *)0x00007fff50b37400: objc_msgSend
    0x7fff50b51c81 <+33>: xorl   %edi, %edi
    0x7fff50b51c83 <+35>: movq   %rdi, %rax
    0x7fff50b51c86 <+38>: retq   
    0x7fff50b51c87 <+39>: nop    
Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:]:
    0x7fff2590ddf0 <+0>:  testq  %rdx, %rdx
    0x7fff2590ddf3 <+3>:  je     0x7fff2590de44            ; <+84>
    0x7fff2590ddf5 <+5>:  pushq  %rbp
    0x7fff2590ddf6 <+6>:  movq   %rsp, %rbp
    0x7fff2590ddf9 <+9>:  pushq  %r15
    0x7fff2590ddfb <+11>: pushq  %r14
    0x7fff2590ddfd <+13>: pushq  %rbx
    0x7fff2590ddfe <+14>: pushq  %rax
    0x7fff2590ddff <+15>: movq   %rdi, %r14
    0x7fff2590de02 <+18>: movq   0x62206d9f(%rip), %rdi    ; (void *)0x00007fff87b1d538: NSConcreteNotification
    0x7fff2590de09 <+25>: movq   0x622003a8(%rip), %rsi    ; "newTempNotificationWithName:object:userInfo:"
    0x7fff2590de10 <+32>: movq   0x5b06b749(%rip), %r15    ; (void *)0x00007fff50b37400: objc_msgSend
    0x7fff2590de17 <+39>: callq  *%r15
    0x7fff2590de1a <+42>: movq   %rax, %rbx
    0x7fff2590de1d <+45>: movq   0x8(%r14), %rdi
    0x7fff2590de21 <+49>: movq   %rax, %rsi
    0x7fff2590de24 <+52>: xorl   %edx, %edx
    0x7fff2590de26 <+54>: callq  0x7fff25ae65d8            ; symbol stub for: _CFXNotificationPost
->  0x7fff2590de2b <+59>: movq   0x6220038e(%rip), %rsi    ; "recycle"
    0x7fff2590de32 <+66>: movq   %rbx, %rdi
    0x7fff2590de35 <+69>: movq   %r15, %rax
    0x7fff2590de38 <+72>: addq   [=14=]x8, %rsp
    0x7fff2590de3c <+76>: popq   %rbx
    0x7fff2590de3d <+77>: popq   %r14
    0x7fff2590de3f <+79>: popq   %r15
    0x7fff2590de41 <+81>: popq   %rbp
    0x7fff2590de42 <+82>: jmpq   *%rax
    0x7fff2590de44 <+84>: retq   

DeviceFieldButton :

class DateFieldButton: UIButton {

    var pattern = String()

    override var isSelected: Bool {
        didSet {
            if #available(iOS 13.0, *) {
                backgroundColor = isHighlighted ?  .highlightKeyboardColor : .keyboardColor
            } else {
                backgroundColor = isHighlighted ?  .highlightLightKeyboard : .lightKeyboard
            }
        }
    }

    override var isHighlighted: Bool {
        didSet {
            if #available(iOS 13.0, *) {
                backgroundColor = isHighlighted ?  .highlightKeyboardColor : .keyboardColor
            } else {
                backgroundColor = isHighlighted ?  .highlightLightKeyboard : .darkKeyboard
            }
        }
    }

}

用法:

            let button = DateFieldButton(type: .custom)
            button.layer.cornerRadius = 10

            dateFormatter.dateFormat = pattern
            let realPattern = dateFormatter.string(from: Date())

            button.setTitle(realPattern, for: .normal)
            button.titleLabel?.text = realPattern
            button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)

            if #available(iOS 13, *) {
                button.setTitleColor(.blackAndWhite, for: .normal)
            }
            else {
                button.setTitleColor(.black, for: .normal)
            }
            button.pattern = pattern

            button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)

经过一天的调试,我找到了崩溃的问题。

之前我是用这个做接收端的,我用的是这个:

NotificationCenter.default.addObserver(self, selector: #selector(dateButtonTapped(_:for:)), name: .dateFieldTapped, object: nil)
@objc func dateButtonTapped(_ sender: Notification, for event: UIEvent) {

}

这会导致模拟器崩溃,如果删除 for 参数,则不会发生这种情况,这是我因懒惰而复制的另一个方法遗留下来的。

NotificationCenter.default.addObserver(self, selector: #selector(dateButtonTapped(_:)), name: .dateFieldTapped, object: nil)
@objc func dateButtonTapped(_ sender: Notification) {

}

所以,我正在回答我自己的问题,以防将来其他人遇到同样的问题。

顺便感谢@Philip Mills 在上面的评论!这让我关注接收端而不是关注发布端。