Swift:Candidate is not '@objc',but protocol requires it分析

案例

class ExamMenu: NSObject {

}

extension ExamMenu: UITableViewDataSource {
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell:UITableViewCell! = tableView.dequeueReusableCellWithIdentifier("UITableViewCell")
        cell.contentView.backgroundColor = .lightGrayColor()
        return cell
    }

}

上面的代码是在swift中实现一个UITableViewDataSource的典型代码,按照上面的书写方式,一切正常,编译器不会报错。但有这时候我们想给ExamMenu添加access level,把它设置为内部私有的,我们把class的定义改成了下面的写法:

private class ExamMenu: NSObject {

}

error.png

这时我们看到编译器给我们报错了:Candidate is not '@objc',but protocol requires it,意思是协议要求方法采用@objc修饰。我们按照编译器提示,在协议方法前面加上@objc修饰,错误消失了。

extension ExamMenu: UITableViewDataSource {
    
    @objc func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    
    @objc func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell:UITableViewCell! = tableView.dequeueReusableCellWithIdentifier("UITableViewCell")
        cell.contentView.backgroundColor = .lightGrayColor()
        return cell
    }

}

分析

我们知道,为方法添加上@objc修饰之后,表明该方法可能被OC的调用,OC的方法调最终会编译成objc_msgSend方法调用,基于动态的方式去查找方法。为了提高性能,swift的方法调用是静态调用方式,所以swift中的方法如果不加任何表明,就不能给OC的runtime查找到,采用@objc修饰也就是建议编译器将该方法放入动态查找列表(但编译器可能会做优化,而没有这么做),好让OC能够动态查找到该方法。

回到刚才的案例中,我们还有个疑问:为什么在给class加上private修复之前,编译器没有报错?

这里是因为ExamMenu继承自NSObject,编译器会其自动为所有的非private成员加上@objc修饰,而如果给class加上了private修饰,其成员默认就是private访问控制权限,就缺少了@objc的修饰,就需要我们手动添加上。

dynamic修饰符

swift中dynamic修饰符也是为了告诉编译器,方法可能被动态调用,需要将其添加到查找表中,所以,将代码按照如下修改,也可以解决编译器报错的问题。

extension ExamMenu: UITableViewDataSource {
    
    dynamic func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    
    dynamic func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell:UITableViewCell! = tableView.dequeueReusableCellWithIdentifier("UITableViewCell")
        cell.contentView.backgroundColor = .lightGrayColor()
        return cell
    }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容