Swift Runtime
我们都知道 Objective-C 是一门动态的语言,有的时候我们会使用 Runtime 处理一些在 Objective-C 上面无法实现或者很难实现的功能。例如:在扩展中添加属性;动态的获取属性的名称,方法名等。那么究竟什么是 Runtime?
什么是 Runtime ?
Runtime 简称运行时。Objective-C 就是运行时机制,也就是在程序运行时候的一些机制,其中最主要的是消息机制。对于我们熟悉的C语言,函数的调用在编译的时候会决定调用哪个函数。但对于 Objective-C 的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
也就有了下面这两点结论:
- 在编译阶段,Objective-C 可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
 - 在编译阶段,C语言调用未实现的函数就会报错。
 
Swift 中的 Runtime
好了上面说了这么多都是在说 Objective-C 的 Runtime,那么 Swift 的 Runtime 是什么样的呢?我们来写几行代码看一下。
1  | class Demo {  | 
猜一下,会输出什么?当然,这里什么都不输出!为什么?因为我们创建的类 Demo 是一个纯 Swift 的类,因为 Swift 是一门静态语言,所以我们对其运用 Runtime 机制当然是不可能获取到 Demo 类的属性的。如果我非要在 Swift 代码中运用 Runtime 技术呢?当然是有办法的。我们可以在你要获取的属性(函数同理)前用 @objc 修饰即可(当然加上 dynamic 也是可以的,不过编译器会提示错误,必须要在 dynamic 前添加 @objc),就像如下这样:
1  | class Demo {  | 
我们在添加如下的代码来打印出方法名:
1  | var fCount: UInt32 = 0  | 
输出如下:
1  | Property: name  | 
为什么加上 @objc 就有 Runtime 机制了呢?Swift 不是静态语言吗?
我们来看一下官方文档里对 @objc 是怎么说的。
把这个特性用到任何可以在 Objective-C 中表示的声明上——例如,非内嵌类,协议,非泛型枚举(原始值类型只能是整数),类和协议的属性、方法(包括
setter和getter),初始化> > 器,反初始化器,下标。objc特性告诉编译器,这个声明在 Objective-C 代码中是可用的。给扩展应用这个特性与为这个扩展中所有不显式标记为 nonobjc 特性的成员应用是一样的效果。
用
objc特性标记的类必须继承自一个 Objective-C 中定义的类。如果你把objc用到类或协议中,它会隐式地应用于该类或协议中 Objective-C 兼容的成员上。如果一个类继承自另一个> 带objc特性标记或 Objective-C 中定义的类,编译器也会隐式地给这个类添加objc特性。标记为objc特性的协议不能继承自非objc特性的协议。
objc特性同样会在下面的情况中隐式地添加:
- 声明是子类的重写,并且父类的声明有
 objc特性;- 声明满足的需求来自一个拥有
 objc特性的协议;- 声明有
 IBAction,IBOutlet,IBDesignable,IBInspectable,NSManaged, 或者GKInspectable特性。如果你在一个枚举中使用
objc特性,枚举名和每个成员名串联起来,作为枚举成员暴露给 Objective-C 代码。成员名首字母大写。例如,一个 SwiftPlanet枚举成员叫做venus,> 它作为一个叫PlanetVenus的成员暴露到 Objective-C 代码中。
objc特性可以接受一个特性实参,由一个标识符组成。当你想在 Objective-C 中为objc特性标记的实体暴露一个不同的名字时,用这个特性。你可以把这个实参用在命名类,枚举,枚举成> 员,协议,方法,getter,setter,初始化器。下面的例子把 ExampleClass 中 enabled 属性的getter作为 isEnabled 暴露给 Objective-C 代码,而不仅仅是属性本身的名字。
 1
2
3
4
5
6
7
8 @objc
class ExampleClass: NSObject {
var enabled: Bool {
@objc(isEnabled) get {
// Return the appropriate value
}
}
}
首先有 @objc 这个关键字,它是用来将 Swift 的 API 暴漏给 Objective-C 和 Runtime 使用的,文档里也很清楚的说明了,如果你类继承自 Objective-C 的类,这个标识符就会被自动加进去,加了这标识符的属性、方法无法保证都会被运行时调用,因为 Swift 会做静态优化,想要完全被声明成动态调用,必须使用 dynamic 标识符修饰,当然添加了 dynamic 的时候,它会自己在加上 @objc 这个标识符。
举个例子:
1  | class Demo: NSObject {  | 
虽然这时我们没有在 dBool 和 dInt 两个属性添加 @objc 修饰,但是 Runtime 时依然能够获取到,因为类继承了 Objective-C 中的 NSObject,他会隐式的在属相前面添加 @objc,同理,继承自 UIViewController 等的类,都有这个特性。