在上线的当时晚上,测验发现有一个按钮点击无呼应,测验给出的复现途径是:在release包里,进入到对应界面,键盘弹出按钮可正常点击,键盘收起按钮无法点击,debug包也能够正常点击。假如你对这个bug感兴趣,点击下载最简化的demo.
其间可复现的核心代码如下
class ViewController: UIViewController {
var btn: UIButton = {
let btn = UIButton(type: .system)
btn.frame = CGRect(x: 100, y: 100, width: 60, height: 40)
btn.setTitle("点我", for: .normal)
btn.addTarget(self, action: #selector(onTap), for: .touchUpInside)
return btn
}()
var textField: UITextField = {
let textField = UITextField()
textField.placeholder = "请输入"
textField.frame = CGRect(x: 100, y: 160, width: 60, height: 40)
return textField
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
view.addSubview(btn)
view.addSubview(textField)
}
@objc func onTap() {
print("onTap")
}
}
extension UIWindow {
#if DEBUG
#else
open override var canBecomeFirstResponder: Bool {
return true
}
#endif
}
在看下面的解释之前,你能够先看看上面的代码,是否应该能复现出上述的bug?
当然,答案一定是肯定会出现上面的问题。当然你可能会有以下的疑问?
- 为什么debug的时分onTap办法会被调用?
- 为什么键盘弹起的时分onTap办法会被调用?
在上面的代码中,btn原意是要写成懒加载的,成果由于仿制失误,导致仿制的时分,少仿制了一个lazy,导致btn变成了非懒加载的存储变量,后边变成了一个当即履行的闭包。在闭包履行的时分ViewController还未进行初始化完结,闭包中的self其实是一个Function, 由于btn的target会测验转为NSObject,这儿一定转化不成功,咱们这儿简单认定为btn的target等于nil。
也就是说上面的代码能够简单的等同于btn.addTarget(nil, action: #selector(onTap), for: .touchUpInside)
.
而当对一个按钮增加事情的时分,假如target为nil,系统会进行一下的操作
- 首先获取APP当时的firstResponder,假如firstResponder为nil,则进行第4步,不然进行下一步
- 判别firstResponder是否完成此target对应的action,假如完成了,则进行调用。
- 不然则判别firstResponder的nextResponder是否完成,如此循环判别,一直到nextResponder为nil
- 判别btn是否完成了对应的action,假如完成,则进行调用。
- 不然则判别btn的nextResponder是否完成了对应的action,如此循环判别,一直到nextResponder为nil
关于UIButton的addTarget,更多信息可检查对UIButton的addTarget办法探求
回到了上面的代码:
当是debug的时分,进入到这个页面firstResponder等于nil,会走到第4步,而btn明显没有完成onTap办法,因而会找它的nextResponder,他的nextResponder是VC的View,也没有完成onTap办法,可是VC的View的nextResponder是ViewController,完成了onTap办法,因而onTap会被调用。
当release的时分,进入到这个页面firstResponder等于keyWindow,按照上面的过程,什么也不会履行。(上面的扩展中对UIWindow增加扩展,是为了摇一摇,而只在release增加,是因为咱们项目中接入了RN,RN在debug中hook了系统的事情,咱们项目中做了一些额外的处理,当然不是很重要)
当release的时分,进入到这个页面并且弹起键盘的时分,firstResponder就等于了textField,会走第一步,textField的nextResponder的nextResponder也是VC,因而ViewController的onTap办法也会被调用
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。