Swift Runtime探索
class XQTeacher {
var age = 18
func teach() {
print("teach")
}
}
func test() {
var properCount : UInt32 = 0
let properList = class_copyPropertyList(XQTeacher.self, &properCount)
for i in 0..<numericCast(properCount) {
if let proper = properList?[i] {
let properName = property_getName(proper)
print("属性:\(String(utf8String: properName)!)")
}else{
print("没有属性")
}
}
var methodCount : UInt32 = 0
let methodList = class_copyMethodList(XQTeacher.self, &methodCount)
for i in 0..<numericCast(methodCount) {
if let method = methodList?[i] {
let methodName = method_getName(method)
print("方法名:\(String(describing: methodName))")
}else{
print("没有方法")
}
}
}
test()
print("end")
- 运行这段代码你会发现,当前不管是我们的方法列表还是我们的属性列表,此次此刻都是为空的
- 在Swift值类型和引用类型、方法调度中我们知道
@objc
的作用,如果这个时候我们将我们当前的方法和属性添加上@objc
,会发生什么?
- 此刻代码会输出我们当前的
teach
方法和age
属性。但是此刻对于我们的oc来说是没有办法使用的,还得将class
继承自NSObject
总结
- 对于纯
Swift
类来说,是不具备动态特性。方法和属性不加任何修饰符的情况下。这个时候其实已经不具备我们所谓的runtime
特性了。
- 对于纯
Swift
类,方法和属性添加@objc
标识的情况下,当前我们可以通过Runtime API
拿到,但是在oc中是没法进行调度的。
- 对于继承自
NSObject
类来说,如果我们想要动态的获取当前的属性和方法,必须在其声明前添加@objc
关键字,方法交换: dynamic
的标识。否则也是没有办法通过Runtime API
获取的。
SwiftObject
- 通过源码调试,在
class_copyPropertyList
方法执行的时候打下断点,运行进入断点
- 进到
class_copyPropertyList
方法
- 进入到
data
方法,可以看到swift
有一个默认的基类_SwiftObject
- 在
Swift
源码里面搜索_SwiftObject
TargetAnyClassMetadata
里面的成员
Swift
为了和oc交互,在底层存储的数据结构上是和oc保持了部分一致。
- 在
objc
源码中也有swift_class_t
的定义,它是一个继承自objc_class
的结构体,也就是在objc_class
的成员变量的基础上加上自己的成员变量
反射
- 反射就是可以动态获取类型、成员信息,在运行时可以调用方法、属性等行为的特性。上面我们分析过了,对于一个纯
Swift
类来说,并不支持我们直接像oc那样操作;但是Swift
标准库依然提供了反射机制让我们访问成员信息, 反射的用法非常简单。
class XQTeacher {
var age = 18
}
var t = XQTeacher()
let mir = Mirror(reflecting: t)
for pro in mir.children {
print("\(String(describing: pro.label)):\(pro.value)")
}
func test(_ obj:Any) -> Any {
let mirror = Mirror(reflecting: obj)
guard !mirror.children.isEmpty else {
return obj;
}
var keyValue : [String:Any] = [:]
for child in mirror.children {
if let keyName = child.label {
keyValue[keyName] = test(child.value)
}else{
print("label为空")
}
}
print(keyValue)
return keyValue
}
test(t)
protocol CustomJSONMap {
func jsonMap() -> Any
}
extension CustomJSONMap {
func jsonMap() -> Any {
let mirror = Mirror(reflecting: self)
guard !mirror.children.isEmpty else {
return self;
}
var keyValue : [String:Any] = [:]
for child in mirror.children {
if let value = child.value as? CustomJSONMap {
if let keyName = child.label {
keyValue[keyName] = value.jsonMap()
}else{
print("label为空")
}
}else{
print("没有遵守协议")
}
}
print(keyValue)
return keyValue
}
}
class XQTeacher : CustomJSONMap{
@objc var age = 18
@objc func teach() {
print("teach")
}
}
extension Int : CustomJSONMap {}
var t = XQTeacher()
let result = t.jsonMap()
---------
错误处理
- 在上文的json解析中,我们没有对错误进行处理只是打印了,定义一个
JSONMapError
,错误的抛出我们一般使用throw
。
enum JSONMapError : Error {
case emptyKey
case notConformProtocol
}
protocol CustomJSONMap {
func jsonMap() throws -> Any
}
extension CustomJSONMap {
func jsonMap() throws -> Any {
let mirror = Mirror(reflecting: self)
guard !mirror.children.isEmpty else {
return self;
}
var keyValue : [String:Any] = [:]
for child in mirror.children {
if let value = child.value as? CustomJSONMap {
if let keyName = child.label {
keyValue[keyName] = try? value.jsonMap()
}else{
throw JSONMapError.emptyKey
}
}else{
throw JSONMapError.notConformProtocol
}
}
return keyValue
}
}
class XQTeacher : CustomJSONMap{
@objc var age = 18
var height = 1.98
@objc func teach() {
print("teach")
}
}
extension Int : CustomJSONMap {}
var t = XQTeacher()
let result = try? t.jsonMap()
print(result)
元类型、AnyClass、Self
AnyObject
:代表任意类的instance
,类的类型,仅类遵守的协议
Any
:代表任意类型,包括funcation
类型或者Optional
类型
AnyClass
:代表任意实例的类型
T.self
:如果T
是实例对象,返回的就是它本身;如果T
是类,返回的就是元类型(MetaData)
T.Type
:一种类型,T.self
是T.Type
类型
type(of:)
:用来获取一个值的动态类型