定义两个类SwifitClassA、SwifitClassB让他们尽量多的包含Swifit类型(Character、String、AnyObject、Tuple)
class SwifitClassA {
var aBool: Bool = true
var aInt: UInt = 0
var aFloat: Float = 23.45
var aDouble: Double = 455.654534
var aString: String = "string"
var aObject: AnyObject! = nil
//使用dynamic/@objc修饰就可以使用OC的runtime特性
func testReturnVoidWithaId(aId:UIView) {
}
//不能使用dynamic/@objc修饰,返回值为OC不识别的元组类型
func testReturnCharactor() -> (Int, Bool) {
let aInt:Int = 4
let aBool:Bool = true
return (aInt, aBool)
}
//不能使用dynamic/@objc修饰有OC不识别的类型Character
func testReturnVoidWithCharacter(aCharacter:Character) {
}
}
class SwifitClassB:UIViewController {
var aBool: Bool = true
var aInt: UInt = 0
var aFloat: Float = 23.45
var aDouble: Double = 455.654534
var aString: String = "string"
var aObject: AnyObject! = nil
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}
func testReturnVoidWithaId(aId:UIView) {
}
func testReturnVoidWithaBool(aBOOl:Bool, aInt:UInt, aFloat:Float, aDouble:Double, aString:String, aObject:AnyObject) {
}
//runTime不能获取有OC不识别的元组类型
func testReturnTuple(aBool:Bool, aInt:Int, afloat:Float) -> (Bool, Int, Float) {
return (aBool, aInt, aFloat)
}
//runTime不能获取有OC不识别的Character类型
func testReturnVoidWithCharactor(aCharactor:Character) {
}
func tableview(table:UITableView, numberOfRowsInSection section:Int) -> Int {
return 20
}
}
动态获取SwifitClassA、SwifitClassB的属性和方法的实现
func showClassRunTime(cls:AnyClass) {
print("<---------start methodList(\(cls))--------->")
var methodNum:UInt32 = 0
let methodList = class_copyMethodList(cls, &methodNum)
for index in 0..let method:Method = methodList[Int(index)]
print(String(UTF8String: method_getTypeEncoding(method)))
print(String(UTF8String: method_copyReturnType(method)))
print(String(_sel:method_getName(method)))
print("----")
}
print("<---------end methodList(\(cls))--------->")
print("\n")
print("<---------start propertyList(\(cls))--------->")
var propertyNum:UInt32 = 0
let propertyList = class_copyPropertyList(cls, &propertyNum)
for index in 0..let property:objc_property_t = propertyList[Int(index)]
print(String(UTF8String: property_getName(property)))
print(String(UTF8String: property_getAttributes(property)))
print("----")
}
print("<---------end propertyList(\(cls))--------->")
}
方法调用
let swifitClassA: SwifitClassA = SwifitClassA()
let swifitClassB: SwifitClassB = SwifitClassB()
showClassRunTime(object_getClass(swifitClassA))
print("\n==============================================\n")
showClassRunTime(object_getClass(swifitClassB))
OK,让我们来看看结果是什么
<---------start methodList(SwifitClassA)--------->
<---------end methodList(SwifitClassA)--------->
<---------start propertyList(SwifitClassA)--------->
<---------end propertyList(SwifitClassA)--------->
==============================================
<---------start methodList(SwifitClassB)--------->
Optional("B16@0:8") Optional("B")
aBool
----
Optional("v20@0:8B16") Optional("v")
setABool:
----
Optional("Q16@0:8") Optional("Q")
aInt
----
Optional("v24@0:8Q16") Optional("v")
setAInt:
----
Optional("f16@0:8") Optional("f")
aFloat
----
Optional("v20@0:8f16") Optional("v")
setAFloat:
----
Optional("d16@0:8") Optional("d")
aDouble
----
Optional("v24@0:8d16")
Optional("v")
setADouble:
----
Optional("@16@0:8") Optional("@")
aString
----
Optional("v24@0:8@16") Optional("v")
setAString:
----
Optional("@16@0:8") Optional("@")
aObject
----
Optional("v24@0:8@16") Optional("v")
setAObject:
----
Optional("v24@0:8@16") Optional("v")
testReturnVoidWithaId:
----
Optional("v56@0:8B16Q20f28d32@40@48") Optional("v")
testReturnVoidWithaBool:aInt:aFloat:aDouble:aString:aObject:
----
Optional("q32@0:8@16q24") Optional("q")
tableview:numberOfRowsInSection:
----
Optional("@32@0:8@16@24") Optional("@")
initWithNibName:bundle:
----
Optional("v16@0:8") Optional("v")
viewDidLoad
----
Optional("v20@0:8B16") Optional("v")
viewDidAppear:
----
Optional("@?") Optional("@?")
.cxx_destruct
----
Optional("@24@0:8@16") Optional("@")
initWithCoder:
----
<---------end methodList(SwifitClassB)--------->
<---------start propertyList(SwifitClassB)--------->
Optional("aBool") Optional("TB,N,VaBool")
----
Optional("aInt") Optional("TQ,N,VaInt")
----
Optional("aFloat") Optional("Tf,N,VaFloat")
----
Optional("aDouble") Optional("Td,N,VaDouble")
----
Optional("aString") Optional("T@\"NSString\",N,C,VaString")
----
Optional("aObject") Optional("T@,N,&,VaObject")
----
<---------end propertyList(SwifitClassB)--------->
观察输出结果:
这是为什么?
纯Swift类的函数调用已经不再是Objective-c的运行时发消息,而是类似C++的vtable,在编译时就确定了调用哪个函数,所以没法通过runtime获取方法、属性。
SwifitClassB继承自UIViewController,基类NSObject,而Swift为了兼容Objective-C,凡是继承自NSObject的类都会保留其动态性,所以我们能通过runtime拿到他的方法。
但为什么testReturnTuple testReturnVoidWithaCharacter却又获取不到呢?
动态性最常用的就是方法替换(Method Swizzling),将类的某个方法替换成自定义的方法。
对于纯Swift类(如TestASwiftClass)来说,无法通过objc runtime替换方法,因为由上面的测试可知拿不到这些方法、属性
对于继承自NSObject类(如TestSwiftVC)来说,无法通过runtime获取到的方法肯定没法替换了。那能通过runtime获取到的方法就都能被替换吗?我们测一把。
我们替换两个可以被runtime获取到的方法:viewDidAppear和testReturnVoidWithaId
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
methodSwizzing(object_getClass(self), originalSelector: Selector("viewDidAppear:"), swizzingSelector: Selector("sz_viewDidAppear:"))
methodSwizzing(object_getClass(self), originalSelector: Selector("testReturnVoidWithId:"), swizzingSelector: Selector("sz_testReturnVoidWithId:"))
}
func methodSwizzing(cls:AnyClass, originalSelector:Selector, swizzingSelector:Selector) {
let originalMethod = class_getInstanceMethod(cls, originalSelector)
let swizzingMethod = class_getInstanceMethod(cls, swizzingSelector)
let didAddMethod = class_addMethod(cls, originalSelector, method_getImplementation(swizzingMethod), method_getTypeEncoding(swizzingMethod))
if didAddMethod {
class_replaceMethod(cls, swizzingSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
}else {
method_exchangeImplementations(originalMethod, swizzingMethod)
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
print("F:\(__FUNCTION__) L:\(__LINE__)")
}
func sz_viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
print("F:\(__FUNCTION__) L:\(__LINE__)")
}
func testReturnVoidWithId(aId:UIView) {
print("F:\(__FUNCTION__) L:\(__LINE__)")
}
func sz_testReturnVoidWithId(aId:UIView) {
print("F:\(__FUNCTION__) L:\(__LINE__)")
}
说明viewDidAppear已经被替换,但是testReturnVoidWithaId却没有被替换,这是为何?
查阅资料发现需要在属性和方法前面加上@obje或dynamic才能被动态替换
我们在方法前加上@objc测试一把试试
@objc func testReturnVoidWithId(aId:UIView) {
print("F:\(__FUNCTION__) L:\(__LINE__)")
}
@objc func sz_testReturnVoidWithId(aId:UIView) {
print("F:\(__FUNCTION__) L:\(__LINE__)")
}
哦?好像是没起作用,why?
文档中有解释:
这也就解释了为什么testReturnVoidWithaId无法被替换,因为写在Swift里的代码直接被编译优化成静态调用了。
我们再试试dynamic
dynamic func testReturnVoidWithId(aId:UIView) {
print("F:\(__FUNCTION__) L:\(__LINE__)")
}
dynamic func sz_testReturnVoidWithId(aId:UIView) {
print("F:\(__FUNCTION__) L:\(__LINE__)")
}
OK,已经可以替换了
<---------start methodList(SwifitClassA)--------->
Optional("B16@0:8")
Optional("B")
aBool
----
Optional("v20@0:8B16")
Optional("v")
setABool:
----
Optional("Q16@0:8")
Optional("Q")
aInt
----
Optional("v24@0:8Q16")
Optional("v")
setAInt:
----
Optional("f16@0:8")
Optional("f")
aFloat
----
Optional("v20@0:8f16")
Optional("v")
setAFloat:
----
Optional("d16@0:8")
Optional("d")
aDouble
----
Optional("v24@0:8d16")
Optional("v")
setADouble:
----
Optional("@16@0:8")
Optional("@")
aString
----
Optional("v24@0:8@16")
Optional("v")
setAString:
----
Optional("@16@0:8")
Optional("@")
aObject
----
Optional("v24@0:8@16")
Optional("v")
setAObject:
----
Optional("v24@0:8@16")
Optional("v")
testReturnVoidWithaId:
----
<---------end methodList(SwifitClassA)--------->
<---------start propertyList(SwifitClassA)--------->
Optional("aBool") Optional("TB,N,VaBool")
----
Optional("aInt") Optional("TQ,N,VaInt")
----
Optional("aFloat") Optional("Tf,N,VaFloat")
----
Optional("aDouble") Optional("Td,N,VaDouble")
----
Optional("aString") Optional("T@\"NSString\",N,C,VaString")
----
Optional("aObject") Optional("T@,N,&,VaObject")
----
<---------end propertyList(SwifitClassA)--------->
可以发现SwifitClassA的属性和方法也已经可以获取了
纯Swift类没有动态性,但在方法、属性前添加dynamic修饰可以获得动态性。
继承自NSObject的Swift类,其继承自父类的方法具有动态性,其他自定义方法、属性需要加dynamic修饰才可以获得动态性。
若方法的参数、属性类型为Swift特有、无法映射到Objective-C的类型(如Character、Tuple),则此方法、属性无法添加dynamic修饰(会编译错误)