美文网首页Swiftselector
swift那神奇的#selector

swift那神奇的#selector

作者: hmisty | 来源:发表于2017-04-01 19:09 被阅读1056次

    第一章 神奇

    猜猜哪个btnTapped函数会被调用?

    import UIKit
    
    class DemoViewController : UIViewController {
        
        override func viewDidLoad() {
            let view = DemoView()
            let btn = view.btn;
            self.view.addSubview(btn!)
        }
        
        @objc func btnTapped(_ sender: UIButton) {
            print("print: surprise!")
        }
    
    }
    
    class DemoView {
        var btn : UIButton!
    
        init() {
            btn = UIButton()
            btn.frame = CGRect(x: 110, y: 70, width: 100, height: 44)
            btn.backgroundColor = UIColor.blue
            btn.setTitle("Press me", for: .normal)
            btn.setTitle("I'm Pressed", for: .highlighted)
            btn.addTarget(self, action: #selector(DemoView.btnTapped(_:)), for: .touchUpInside)
        }
        
        @objc func btnTapped(_ sender: UIButton) {
            print("print: demo button is tapped")
        }
        
    }
    

    答案是:surprise!

    第二章 解惑

    正确的写法应该用singleton模式来实现DemoView,如下。

    import UIKit
    
    class DemoViewController : UIViewController {
        
        override func viewDidLoad() {
            let view = DemoView._instance
            let btn = view.btn;
            self.view.addSubview(btn!)
        }
        
        @objc func btnTapped(_ sender: UIButton) {
            print("print: surprise!")
        }
    
    }
    
    class DemoView {
        var btn : UIButton!
    
        static let _instance = DemoView()
        
        private init() {
            btn = UIButton()
            btn.frame = CGRect(x: 110, y: 70, width: 100, height: 44)
            btn.backgroundColor = UIColor.blue
            btn.setTitle("Press me", for: .normal)
            btn.setTitle("I'm Pressed", for: .highlighted)
            btn.addTarget(self, action: #selector(DemoView.btnTapped), for: .touchUpInside)
        }
        
        @objc func btnTapped(_ sender: UIButton) {
            print("print: demo button is tapped")
        }
        
    }
    

    <b>洞察:</b>

    • Selector是runtime时延迟动态绑定,#selector糖里面只是为了compiler帮助检查prototype正确性(对以前的字符串写法”btnTapped:”无法在编译期检查问题的优化),所以即使写成DemoView.btnTapped,也只是告诉compiler这个原型参考函数,而并非Selector在runtime时真正选取的函数。
      因为函数选取是在Obj-C名字空间里做,所以需要用@objc修饰需要暴露给Obj-C的函数。
    • addTarget的第一个参数是Obj-C消息机制的receiver对象,把selector选取的函数发送给对象,其实就是“对象的方法调用”。
    • self也是运行时选取,所以才会出现神奇的现象,即调用了DemoViewController而不是DemoView的btnTapped函数。
      也因此,如果把上面错误写法中的btn.addTarget(self改为btn.addTarget(DemoView.self,即强行要求调用DemoView对象(instance of the class)的函数,运行时点击按钮就会得到错误,unrecognized selector +[DemoView btnTapped:]。
    • BTW,可以看到,因为swift的lazy initialization特性,所以实现singleton异常简单。

    第三章 祛魅

    Debug容易成为一门玄学。以下是国内外网友分享的一些“迷信“。

    • #selector选择的类必须继承NSObject,也就是要写成class DemoView : NSObject
      实测:假。

    • 原型要写成btnTapped(_:)。
      实测:假。

    第四章 优雅

    通过extension Selector可以写的更加优雅。

    import UIKit
    
    class DemoViewController : UIViewController {
        
        override func viewDidLoad() {
            let view = DemoView._instance
            let btn = view.btn;
            self.view.addSubview(btn!)
        }
        
        @objc func btnTapped(_ sender: UIButton) {
            print("print: surprise!")
        }
    
    }
    
    class DemoView {
        var btn : UIButton!
    
        static let _instance = DemoView()
        
        private init() {
            btn = UIButton()
            btn.frame = CGRect(x: 110, y: 70, width: 100, height: 44)
            btn.backgroundColor = UIColor.blue
            btn.setTitle("Press me", for: .normal)
            btn.setTitle("I'm Pressed", for: .highlighted)
            btn.addTarget(self, action: .btnTapped, for: .touchUpInside)
        }
        
        @objc func btnTapped(_ sender: UIButton) {
            print("print: demo button is tapped")
        }
        
    }
    
    private extension Selector {
        static let btnTapped = #selector(DemoView.btnTapped)
    }
    

    <b>解读:</b>
    action:后面直接用点号的写法是一种糖,compiler看起来就是Selector.btnTapped,也正是最下方的extension代码所实现的。

    From painful to painless.

    相关文章

      网友评论

        本文标题:swift那神奇的#selector

        本文链接:https://www.haomeiwen.com/subject/wyqwottx.html