通过闭包初始化UIButton时selector点击事件偶发无法触发问题
问题根因分析
1. 非lazy属性的初始化时机缺陷
代码中使用let声明的nextButton属于存储属性,通过闭包完成初始化时,闭包的执行时机处于类实例的初始化流程全部完成之前:
- 此时
self指针还未完全就绪,处于不稳定状态。早期Swift编译器存在校验漏洞,没有对这种在非lazy属性初始化闭包中直接引用self的行为做强制拦截,允许代码正常编译 - 运行时的内存布局存在随机性:绝大多数场景下,未就绪的
self指针刚好指向即将完成初始化的ViewController实例地址,addTarget的绑定逻辑可以正常生效;少数场景下self为无效地址甚至空值,导致事件绑定失败,用户点击按钮后不会触发回调
2. lazy属性修复的原理
将nextButton修改为lazy var后,该属性的初始化闭包会推迟到第一次被访问时才执行,也就是viewDidLoad中执行view.addSubview(nextButton)的时刻才会完成按钮的初始化。此时ViewController实例已经完成了全部初始化流程,self指针完全有效,addTarget的绑定逻辑可以100%正常执行,因此问题被彻底修复。
补充说明
当前新版Swift编译器已经完善了校验逻辑,会对这类在非lazy属性初始化闭包中引用self的代码直接抛出编译错误,从编译层面就避免了同类问题的产生。所有需要在属性初始化阶段绑定target、引用实例自身的场景,都应该使用lazy属性,保证初始化时self已经就绪。
内容的提问来源于stack exchange,提问作者Randall
相关产品推荐
相关产品推荐

