Objective-C 和 Swift 在底层使用的是两套彻底不一样的机制,Cocoa 中的 Objective-C 对象是基于运行时的,它从骨子里遵循了 KVC (Key-Value Coding,经过相似字典的方式存储对象信息) 以及动态派发 (Dynamic Dispatch,在运行调用时再决定实际调用的具体实现)。而 Swift 为了追求性能,若是没有特殊须要的话,是不会在运行时再来决定这些的。也就是说,Swift 类型的成员或者方法在编译时就已经决定,而运行时便再也不须要通过一次查找,而能够直接使用。swift
显而易见,这带来的问题是若是咱们要使用 Objective-C 的代码或者特性来调用纯 Swift 的类型时候,咱们会由于找不到所须要的这些运行时信息,而致使失败。解决起来也很简单,在 Swift 类型文件中,咱们能够将须要暴露给 Objective-C 使用的任何地方 (包括类,属性和方法等) 的声明前面加上@objc
修饰符。注意这个步骤只须要对那些不是继承自 NSObject
的类型进行,若是你用 Swift 写的 class 是继承自 NSObject
的话,Swift 会默认自动为全部的非 private 的类和成员加上 @objc
。这就是说,对一个 NSObject
的子类,你只须要导入相应的头文件就能够在 Objective-C 里使用这个类了。数组
@objc
修饰符的另外一个做用是为 Objective-C 侧从新声明方法或者变量的名字。虽然绝大部分时候自动转换的方法名已经足够好用 (好比会将 Swift 中相似 init(name: String)
的方法转换成 -initWithName:(NSString *)name
这样),可是有时候咱们仍是指望 Objective-C 里使用和 Swift 中不同的方法名或者类的名字,好比 Swift 里这样的一个类:app
class 个人类: NSObject { func 打招呼(名字: String) { print("哈喽,\(名字)") } } 个人类().打招呼("小明")
Objective-C 的话是没法使用中文来进行调用的,所以咱们必须使用 @objc
将其转为 ASCII 才能在 Objective-C 里访问:性能
@objc(MyClass) class 个人类 { @objc(greeting:) func 打招呼(名字: String) { print("哈喽,\(名字)") } }
这样,咱们在 Objective-C 里就能调用 [[MyClass new] greeting:@"XiaoMing"]
这样的代码了 (虽然比起原来一点都很差玩了)。优化
另外,正如上面所说的以及在 Selector 一节中所提到的,即便是NSObject
的子类,Swift 也不会在被标记为 private
的方法或成员上自动加 @objc
,以保证尽可能不使用动态派发来提升代码执行效率。ui
若是咱们肯定使用这些内容的动态特性的话,咱们须要手动给它们加上 @objc
修饰。spa
可是须要注意的是,添加 @objc
修饰符并不意味着这个方法或者属性会变成动态派发,Swift 依然可能会将其优化为静态调用。code
若是你须要和 Objective-C 里动态调用时相同的运行时特性的话,你须要使用的修饰符是 dynamic
。对象
通常状况下在作 app 开发时应该用不上,可是在施展一些像动态替换方法或者运行时再决定实现这样的 "黑魔法" 的时候,咱们就须要用到 dynamic
修饰符了。继承
在 KVO 一节中,咱们提到了一个关于使用 dynamic
的实例。
关于 Swift 和 Objective-C 混用的一个好消息是,随着 Swift 的发展,Apple 正在努力改善 SDK。
在 Objective-C 中添加的 nonnull
和 nullable
,以及泛型的数组和字典等,其实上都是为了使 SDK 更加适合用 Swift 来使用所作的努力,咱们仍是颇有但愿在不久的将来可以摆脱掉这些妥协和束缚的。
本文来自王巍的@OBJC 和 DYNAMIC