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 在上面的评论!这让我关注接收端而不是关注发布端。
我这里有这段代码:
@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 在上面的评论!这让我关注接收端而不是关注发布端。