Swift中使用Objective-C的Runtime

Swift调用Objective-C的Runtime


首先,我们来测试一下使用Swift代码来调用Objective-C Runtime的方法,首先创建两个类,用来做对比,一个是纯的Swift类,另一个继承自NSObject:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//纯Swift类
class SwiftClass {
var aBool = true
var aInt = 3
var aStrig = "sss"
var aObject :AnyObject?

func swiftTest() {

}
}

//继承自NSObject的类
class SwiftWithOCClass :NSObject{
var aBool = true
var aInt = 3
var aStrig = "sss"
var aObject :AnyObject?

func swiftTest(aCharacter:Character) {

}

func justTest() {

}
}

接下来,我们来创建一个方法,用来获取Class的方法和属性,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func getMethodListAndProperties(cls:AnyClass) -> Void {
var methodCount:UInt32 = 0
let methodList = class_copyMethodList(cls, &methodCount)

//打印方法
for i in 0..<Int(methodCount) {
let method = methodList[i]
print(String(UTF8String:method_getTypeEncoding(method)))
print(String(_sel:method_getName(method)))
}
free(methodList)

var propertiesCount:UInt32 = 0
let propertiesList = class_copyPropertyList(cls, &propertiesCount)

//打印属性
for i in 0..<Int(propertiesCount) {
let property = propertiesList[i]
print(String(UTF8String:property_getName(property)))
print(String(UTF8String:property_getAttributes(property)))
}
free(propertiesList)
}

当调用getMethodListAndProperties(SwiftWithOCClass)方法时,其打印结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//打印的方法
Optional("B16@0:8")
aBool
Optional("v20@0:8B16")
setABool:
Optional("q16@0:8")
aInt
Optional("v24@0:8q16")
setAInt:
Optional("@16@0:8")
aStrig
Optional("v24@0:8@16")
setAStrig:
Optional("@16@0:8")
aObject
Optional("v24@0:8@16")
setAObject:
Optional("v16@0:8")
justTest
Optional("@?")
.cxx_destruct
Optional("@16@0:8")
init

//打印的属性
Optional("aBool")
Optional("TB,N,VaBool")
Optional("aInt")
Optional("Tq,N,VaInt")
Optional("aStrig")
Optional("T@\"NSString\",N,C,VaStrig")
Optional("aObject")
Optional("T@,N,&,VaObject")
  • 当调用getMethodListAndProperties(SwiftClass)方法时,我们发现并没有打印结果,既没有获取到纯Swift类的属性和方法;而继承自NSObject的类SwiftWithOCClass则能够获取到属性和方法,翻阅官方文档(如下图),我们就能找到原因,通过继承NSObject,对象可以拥有运行时且可以被看成Objective-C对象。
  • 我们不禁要问,对于纯Swift类,难道就没有办法使用Objective-C运行时了么?答案是否定的,我们可以使用dynamic修饰符,实验一下,在纯Swift类的属性aBool中加入dynamic修饰符,代码如下,我们发现,可以通过运行时找到aBool属性的两个访问器方法以及属性名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class SwiftClass {
dynamic var aBool = true
var aInt = 3
var aStrig = "sss"
var aObject :AnyObject?

func swiftTest() {

}
}

//打印结果
Optional("B16@0:8")
aBool
Optional("v20@0:8B16")
setABool:
Optional("aBool")
Optional("TB,N,VaBool")
  • 而当调用getMethodListAndProperties(SwiftWithOCClass)时,大多数方法都打印出来了,但有一个方法没获取到,为func swiftTest(aCharacter:Character),原因是该方法的参数是Character类型,它是Swift特有的,无法转化(Bridge)为OC的类型,所以无法通过运行时获取到该方法。

load方法


在使用Objective-C的Runtime时,经常会用到Method Swizzling技术,该技术通常在load方法中实现。而在Swift中不允许定义load方法,否则编译器会报错,所以,如果要实现Method Swizzling,有几种解决方案:

  • initialize中来实现,但是需要注意的是initialize可能会被调用多次,所以需要在initialize实现中做一些判断,比如判断是否是当前类,而不是子类,使用dispatch_once来进行操作。
  • 在App Delegate中实现Method Swizzling:在AppDelegate的 application(_:didFinishLaunchingWithOptions:)方法中进行Method Swizzling。