Swift带默认nil的闭包参数通过Selector调用时非nil致崩溃求助
问题排查与解决:Selector调用带可选闭包的@objc方法崩溃
问题场景
你定义了带可选默认闭包的@objc方法:
@objc func postToken(completion: (()-> Void)? = nil) { someBlock { completion?() } }
通过Selector调用时:
let tokenIndicatorGesture = UITapGestureRecognizer(target: self, action: #selector(postToken))
completion并未按预期为nil,而是一个无效的闭包指针:
completion (())? 0x00000001049476f8 MyApp`partial apply forwarder for reabstraction thunk helper from @escaping @callee_unowned @convention(block) () -> () to @escaping @callee_guaranteed () -> () at <compiler-generated>
调用completion?()时触发崩溃。
原因分析
Swift的可选闭包默认值(= nil)是Swift专属特性,Objective-C的Selector消息发送机制无法正确解析该逻辑。当通过Selector调用方法时,ObjC runtime不会传入预期的nil,而是生成了一个无效的闭包包装对象,该对象并非可正常调用的闭包,因此执行completion?()时会崩溃。
解决方法
方案1:拆分方法(推荐)
将供Selector调用的无参方法与业务逻辑方法分离,确保明确传入nil闭包:
// 仅供Selector调用的@objc无参方法 @objc func postToken() { postToken(completion: nil) } // 业务逻辑方法,无需@objc(若无需被ObjC直接调用) func postToken(completion: (() -> Void)? = nil) { someBlock { completion?() } }
方案2:兼容ObjC的闭包类型声明
若必须保留原方法的@objc修饰,将闭包声明为ObjC兼容的@convention(block)类型:
@objc func postToken(completion: (@convention(block) () -> Void)? = nil) { someBlock { completion?() } }
注意:该方式仍可能存在Selector参数解析问题,方案1更稳定可靠。
调试验证建议
- 在
postToken方法开头添加打印:print("completion pointer: \(completion), type: \(type(of: completion))"),确认传入对象的有效性。 - 使用LLDB调试时,执行
po completion查看闭包具体内容,判断是否为无效包装对象。
内容的提问来源于stack exchange,提问作者Anton Unt
相关产品推荐
相关产品推荐

