案例
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 {
}

这时我们看到编译器给我们报错了: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
}
}
网友评论