Mirror反射
Mirror
(反射):可以动态获取类型、成员信息,在运⾏时可以调⽤⽅法、属性等⾏为的特性。
对于⼀个纯Swift
类来说,并不⽀持直接像OC
那样使用Runtime
操作。但Swift
标准库依然提供了反射机制,用来访问成员信息。
访问成员信息
class LGTeacher {
var age: Int = 18
var name: String = "Zang"
}
var t = LGTeacher()
let mirror = Mirror(reflecting: t)
for pro in mirror.children{
print("\(pro.label):\(pro.value)")
}
//输出以下内容:
//Optional("age"):18
//Optional("name"):Zang
上述代码中,
Mirror
反射的是实例对象的成员信息,传入的参数必须是实例对象
传入一个类对象
Mirror(reflecting: LGTeacher)
,编译报错
传入一个类的类型
Mirror(reflecting: LGTeacher.self)
,获取不到任何成员信息
查看Mirror定义
Mirror
是一个结构体
Mirror
的init
方法,接收一个Any
类型参数
Children
是一个AnyCollection
,接收一个泛型Mirror.Child
Mirror.Child
是一个元组类型
JSON解析
class LGTeacher {
var age: Int = 18
var name: String = "Zang"
}
func test(_ obj : Any) -> Any {
let mirror = Mirror(reflecting: obj)
guard !mirror.children.isEmpty else {
return obj
}
var keyValue: [String: Any] = [:]
for children in mirror.children {
if let keyName = children.label {
keyValue[keyName] = test(children.value)
}
else {
print("children.label 为空")
}
}
return keyValue
}
var t = LGTeacher()
print(test(t))
//输出以下内容:
//["name": "Zang", "age": 18]
上述代码中,成功的将实例对象
t
转为字典并输出,但在实际开发中,这样的代码写的相对丑陋,下面就来对它做一个简单的封装
抽取协议
我们预期在每一个属性下都能调用
JSON解析
的方法,所以可以将它抽取成一个协议,然后提供一个默认实现,让类遵守协议
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 children in mirror.children {
if let value = children.value as? CustomJSONMap {
if let keyName = children.label {
keyValue[keyName] = value.jsonMap()
}
else {
print("key是nil")
}
}
else {
print("当前-\(children.value)-没有遵守协议")
}
}
return keyValue
}
}
class LGTeacher : CustomJSONMap {
var age: Int = 18
var name: String = "Zang"
}
var t = LGTeacher()
print(t.jsonMap())
//输出以下内容:
//当前-18-没有遵守协议
//当前-Zang-没有遵守协议
//[:]
上述代码中,因为
age
和name
分别为Int
和String
类型,这些类并没有遵守CustomJSONMap
协议,所以无法输出正确结果
extension Int: CustomJSONMap{}
extension String: CustomJSONMap{}
class LGTeacher: CustomJSONMap {
var age: Int = 18
var name: String = "Zang"
}
var t = LGTeacher()
print(t.jsonMap())
//输出以下内容:
//["age": 18, "name": "Zang"]
修改代码,增加
Int
和String
类型的extension
,让它们也遵守CustomJSONMap
协议,打印结果符合预期
上述代码在实际开发中用来做
JSON解析
还有很大差距。代码本身并不完善,也无法支持复杂的嵌套类型,所以它只是基于Mirror
做的简单案例,用来理解Mirror
到底能做些什么。
错误处理
Error协议
Swift
提供Error
协议来标识当前应⽤程序发⽣错误的情况,不管是struct
、Class
、enum
都可以通过遵循这个协议来表示⼀个错误。Error
的定义如下:
public protocol Error{
}
上述
JSON解析
案例中,有两个未遵守协议
和key是nil
,下面演示如何通过Error
协议处理这两种异常情况
上述代码中,将JSONMapError.emptyKey
和JSONMapError.notConformProtocol
进行return
,以此代替之前两个jsonMap
方法来说,由于返回值是Any
类型,故此我们无法区分返回结果是解析成功的字典,还是错误的枚举
对于异常情况,可以使用
throw
关键字将错误抛出,将代码中的return
改为throw
上图使用
throw
编译报错,因为方法还没有声明成throws
。需要在方法返回值前面增加throws
关键字,告诉方法有错误抛出
方法使用
throws
关键字修饰,调用该方法的代码编译报错。对于有错误抛出的方法,需要在调用方法前使用try
关键字
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 children in mirror.children {
if let value = children.value as? CustomJSONMap {
if let keyName = children.label {
keyValue[keyName] = try value.jsonMap()
}
else {
throw JSONMapError.emptyKey
}
}
else {
throw JSONMapError.notConformProtocol
}
}
return keyValue
}
}
extension Int : CustomJSONMap{}
extension String : CustomJSONMap{}
class LGTeacher : CustomJSONMap {
var age: Int = 18
var name: String = "Zang"
}
var t = LGTeacher()
print(try t.jsonMap())
到这⾥一个完整的
Swift
错误表达⽅式就完成了
try关键字
使⽤
try
关键字是Swift
中错误处理最简便的方式,相当于帅锅。将异常向上抛出,抛给上层函数。使⽤try
关键字有两个注意点:
try?
:返回⼀个可选类型,这⾥的结果要么是成功,返回具体结果。要么是错误,返回nil
。这种方式我们不关心具体是哪⼀类错误,统⼀返回nil
try!
:表示你对这段代码有绝对的⾃信,这⾏代码绝对不会发⽣错误
将
Int
和String
遵守CustomJSONMap
协议的代码注释,打印try? t.jsonMap()
,直接返回nil
,看不出具体错误原因
相同代码,使用
try! t.jsonMap()
测试,程序直接闪退
使用
try t.jsonMap()
,将异常抛给上层函数,如果全程没有函数处理异常,最终抛给main
函数也没办法处理,程序直接闪退
do...catch
Swift
中do...catch
是错误处理的另一种方式
//extension Int : CustomJSONMap{}
//extension String : CustomJSONMap{}
class LGTeacher : CustomJSONMap {
var age: Int = 18
var name: String = "Zang"
}
var t = LGTeacher()
do{
try t.jsonMap()
}catch{
print(error)
}
//输出以下内容:
//notConformProtocol
上述代码中,通过
do
作用域捕获异常,通过catch
作用域处理异常,最终打印出错误类型notConformProtocol
LocalError协议
如果使⽤
Error
协议不能详尽表达错误信息,可以使⽤LocalError
协议,定义如下:
public protocol LocalizedError : Error {
//错误的描述
/// A localized message describing what error occurred.
var errorDescription: String? { get }
//失败的原因
/// A localized message describing the reason for the failure.
var failureReason: String? { get }
//恢复的建议
/// A localized message describing how one might recover from the failure.
var recoverySuggestion: String? { get }
//给开发者的帮助
/// A localized message providing "help" text if the user requests help.
var helpAnchor: String? { get }
}
修改
JSON解析
案例,使用LocalizedError
协议,打印具体的错误描述
enum JSONMapError: Error{
case emptyKey
case notConformProtocol
}
extension JSONMapError: LocalizedError{
var errorDescription: String?{
switch self {
case .emptyKey:
return "key为空"
case .notConformProtocol:
return "没有遵守协议"
}
}
}
//extension Int : CustomJSONMap{}
//extension String : CustomJSONMap{}
class LGTeacher : CustomJSONMap {
var age: Int = 18
var name: String = "Zang"
}
var t = LGTeacher()
do{
try t.jsonMap()
}catch{
print(error.localizedDescription)
}
//输出以下内容:
//没有遵守协议
上述代码中,为
JSONMapError
增加extension
扩展,并遵守LocalizedError
协议,在catch
作用域中打印error.localizedDescription
,最终输出错误描述:没有遵守协议
CustomError协议
CustomNSError
相当于OC
中的NSError
,有三个默认属性:
public protocol CustomNSError : Error {
/// The domain of the error.
static var errorDomain: String { get }
/// The error code within the given domain.
var errorCode: Int { get }
/// The user-info dictionary.
var errorUserInfo: [String : Any] { get }
}
修改
JSON解析
案例,使用CustomNSError
协议,打印错误码
enum JSONMapError: Error{
case emptyKey
case notConformProtocol
}
extension JSONMapError: CustomNSError{
var errorCode: Int{
switch self {
case .emptyKey:
return -1
case .notConformProtocol:
return -2
}
}
}
//extension Int : CustomJSONMap{}
//extension String : CustomJSONMap{}
class LGTeacher : CustomJSONMap {
var age: Int = 18
var name: String = "Zang"
}
var t = LGTeacher()
do{
try t.jsonMap()
}catch{
print("\(String(describing: (error as? CustomNSError)?.errorCode))")
}
//输出以下内容:
//Optional(-2)
上述代码中,为
JSONMapError
增加extension
扩展,并遵守CustomNSError
协议,在catch
作用域中使用as?
关键字将error
强转为CustomNSError
类型,并打印errorCode
,最终输出错误码:Optional(-2)
Mirror源码解析
@_silgen_name关键字
@_silgen_name
关键字用来改变当前方法的调用方式在
swift
项目中,创建test.c
,里面声明lg_add
函数,传入两个参数,返回参数相加的结果
在
main.swift
中,定义swift_lg_add
方法,参数、返回值和test.c
的lg_add
一致,并加入@_silgen_name("lg_add")
关键字声明,尝试调用swift_lg_add
方法,发现最终会调用lg_add
源码解析
打开
Mirror.swift
,可以看到Mirror
是一个结构体类型
结构体有一个初始化方法,传入一个
Any
。这里判断subject
是否符合CustomReflectable
类型,符合由customMirror
确定属性,不符合由系统生成
首先看一下由系统生成的
Mirror
,来到internalReflecting
方法定义,首先通过_getNormalizedType
方法获取类型,再通过_getChildCount
方法获取属性大小,最后通过遍历将属性存储到集合中
搜索
_getNormalizedType
方法,进入ReflectionMirror.swift
文件。该方法用到@_silgen_name
关键字声明,最终会调用swift_reflectionMirror_normalizedType
方法
来到
swift_reflectionMirror_normalizedType
方法定义,有三个参数,value
是Mirror
实例,type
是调用时通过type(of:)
获取动态类型,T
是自己传入的类型。最后调用call
方法返回impl
的type
属性,而impl
是ReflectionMirrorImpl
结构
来到
call
方法定义,将方法T
和passedValue
参数,传给unwrapExistential
方法拿到type
。如果传入的passedType
不为空,就将传入的passedType
赋值给type
来到
unwrapExistential
方法定义,通过getDynamicType
方法获取当前的动态类型。通过内部方法调用,最终发现获取类型的本质是依赖Metadata
元数据。
找到
Metadata
,由于类结构的Metadata
过于复杂,我们下面以结构体TargetStructMetadata
为例,它继承于TargetValueMetadata
来到
TargetValueMetadata
定义,它继承于TargetMetadata
,除了继承下来的kind
属性,还有一个ConstTargetMetadataPointer
类型的description
属性,而description
属性记录的就是有关元数据的描述。ConstTargetMetadataPointer
实际是一个泛型,需要重点分析的是TargetValueTypeDescription
下面介绍元数据的描述,进入
TargetValueTypeDescription
定义,它继承于TargetTypeContextDescription
进入
TargetTypeContextDescription
定义,里面有一个TargetRelativeDirectPointer
类型的Name
属性,用于记录当前类型的名称
进入
TargetRelativeDirectPointer
定义,它是RelativeDirectPointer
类型的别名
进入
RelativeDirectPointer
定义,它是一个模板类,继承于RelativeDirectPointerImpl
,传入类型T
、Nullable
为true
、Offset
为int32_t
进入
RelativeDirectPointerImpl
定义,它有一个int32_t
类型的RelativeOffset
属性
还包含
ValueTy
、PointerTy
,其中ValueTy
代表T
的类型,PointerTy
代表T
的指针类型
回到
RelativeDirectPointer
定义,获取值ValueTy
、获取指针PointerTy
调用的都是this->get()
方法
进入
get
方法,又回到RelativeDirectPointerImpl
定义,方法内调用applyRelativeOffset
方法
进入
applyRelativeOffset
方法,返回base + extendOffset
,base
是当前指针地址,extendOffset
是相对地址(偏移地址),最终相加得到偏移后的地址存储的是类型
回到
call
函数,可以看到很多依赖都是基于call
函数。比如获取Class
数据时,通过callClass
方法得到一个ClassImpl
。如果是元组类型,会得到一个TupleImpl
。如果是结构体,会得到StructImpl
。
以结构体为例,进入
StructImpl
定义,继承于ReflectionMirrorImpl
。其中isReflectable
方法,也是通过Metadata
获取getDescriotion
,找到isReflectable
判断是否支持反射
count
方法,同样通过Metadata
获取getDescriotion
,找到NumFields
获取属性总数
subscipt
方法,获取属性名称和属性值。通过getFieldOffests()[i]
得到一个属性的偏移值。通过getFiledAt
方法拿到属性名称,bytes
是当前value
地址,加上fieldOffset
偏移地址,就是属性的值
进入
getFiledAt
方法,通过baseDesc
的Fields.get()
获取fields
,将fields
赋值给FieldDescriptor
类型的descriptor
,通过descriptor.geyFields[index]
获取一个属性field
,最终通过field.getFieldName()
拿到属性值的名称
Fields
也是一个相对指针的存储,类型是FieldDescriptor
getFieldName
方法调用的是FieldName.get()
,在FieldRecord
类里
下面通过
Swift
代码仿写Mirror
的实现进行原理解析
原理解析
定义
RelativePointer
结构体,仿照RelativeDirectPointerImpl
的get
方法,实现当前指针的偏移
struct RelativePointer<T> {
var offset: Int32
mutating func get() -> UnsafeMutablePointer<T>{
let offset = self.offset
return withUnsafePointer(to: &self) { p in
return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
}
}
}
offset
:偏移地址UnsafeMutablePointer
:转换为UnsafeMutablePointer
类型指针UnsafeRawPointer(p)
:当前this
advanced
:移动步长numericCast
:位的强转assumingMemoryBound
:假定内存绑定为T
的类型
定义
FieldRecordT
结构体,实现类似getFiledAt
方法,在连续内存空间中,移动步长,拿到每一个FieldRecord
struct FieldRecordT<Element> {
var element: Element
mutating func element(at i: Int) -> UnsafeMutablePointer<Element> {
return withUnsafePointer(to: &self) { p in
return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).assumingMemoryBound(to: Element.self).advanced(by: i))
}
}
}
定义
StructMetadata
结构体,相当于TargetStructMetadata
struct StructMetadata{
var kind: Int
var typeDescriptor: UnsafeMutablePointer<StructDescriptor>
}
定义
StructDescriptor
结构体,相当于TargetValueTypeDescription
struct StructDescriptor {
let flags: Int32
let parent: Int32
var name: RelativePointer<CChar>
var AccessFunctionPtr: RelativePointer<UnsafeRawPointer>
var Fields: RelativePointer<FieldDescriptor>
var NumFields: Int32
var FieldOffsetVectorOffset: Int32
}
定义
FieldDescriptor
结构体,相当于FieldDescriptor
struct FieldDescriptor {
var MangledTypeName: RelativePointer<CChar>
var Superclass: RelativePointer<CChar>
var kind: UInt16
var fieldRecordSize: Int16
var numFields: Int32
//连续的存储空间
var fields: FieldRecordT<FieldRecord>
}
定义
FieldRecord
结构体,相当于FieldRecord
struct FieldRecord {
var Flags: Int32
var MangledTypeName: RelativePointer<CChar>
var FieldName: RelativePointer<CChar>
}
定义
LGTeacher
结构体
struct LGTeacher{
var age = 18
var name = "Zang"
}
使用
unsafeBitCast
内存按位转换,将LGTeacher
的类型绑定到StructMetadata
let ptr = unsafeBitCast(LGTeacher.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)
获取类型
let namePtr = ptr.pointee.typeDescriptor.pointee.name.get()
print("类型:\(String(cString: namePtr))")
//输出以下结果:
//类型:LGTeacher
获取属性大小
let fieldDescriptorPtr = ptr.pointee.typeDescriptor.pointee.Fields.get()
print("属性大小:\(ptr.pointee.typeDescriptor.pointee.NumFields)")
//输出以下结果:
//属性大小:2
遍历出所有属性名称
for index in 0..<ptr.pointee.typeDescriptor.pointee.NumFields {
let recordPtr = fieldDescriptorPtr.pointee.fields.element(at: Int(index))
let valOffset=recordPtr.pointee.FieldName.get().pointee
print("属性\(index):\(String(cString: recordPtr.pointee.FieldName.get()))")
}
//输出以下结果:
//属性0:age
//属性1:name