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
Mirror的init方法,接收一个Any类型参数
init
Children是一个AnyCollection,接收一个泛型Mirror.Child
Children
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协议处理这两种异常情况
return 上述代码中,将
JSONMapError.emptyKey和JSONMapError.notConformProtocol进行return,以此代替之前两个jsonMap方法来说,由于返回值是Any类型,故此我们无法区分返回结果是解析成功的字典,还是错误的枚举
对于异常情况,可以使用
throw关键字将错误抛出,将代码中的return改为throw
throw
上图使用
throw编译报错,因为方法还没有声明成throws。需要在方法返回值前面增加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。这种方式我们不关心具体是哪⼀类错误,统⼀返回niltry!:表示你对这段代码有绝对的⾃信,这⾏代码绝对不会发⽣错误
将
Int和String遵守CustomJSONMap协议的代码注释,打印try? t.jsonMap(),直接返回nil,看不出具体错误原因
try?
相同代码,使用
try! t.jsonMap()测试,程序直接闪退
try!
使用
try t.jsonMap(),将异常抛给上层函数,如果全程没有函数处理异常,最终抛给main函数也没办法处理,程序直接闪退
try
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函数,传入两个参数,返回参数相加的结果
lg_add
在
main.swift中,定义swift_lg_add方法,参数、返回值和test.c的lg_add一致,并加入@_silgen_name("lg_add")关键字声明,尝试调用swift_lg_add方法,发现最终会调用lg_add
swift_lg_add
源码解析
打开
Mirror.swift,可以看到Mirror是一个结构体类型
Mirror
结构体有一个初始化方法,传入一个
Any。这里判断subject是否符合CustomReflectable类型,符合由customMirror确定属性,不符合由系统生成
init
首先看一下由系统生成的
Mirror,来到internalReflecting方法定义,首先通过_getNormalizedType方法获取类型,再通过_getChildCount方法获取属性大小,最后通过遍历将属性存储到集合中
internalReflecting
搜索
_getNormalizedType方法,进入ReflectionMirror.swift文件。该方法用到@_silgen_name关键字声明,最终会调用swift_reflectionMirror_normalizedType方法
_getNormalizedType
来到
swift_reflectionMirror_normalizedType方法定义,有三个参数,value是Mirror实例,type是调用时通过type(of:)获取动态类型,T是自己传入的类型。最后调用call方法返回impl的type属性,而impl是ReflectionMirrorImpl结构
swift_reflectionMirror_normalizedType
来到
call方法定义,将方法T和passedValue参数,传给unwrapExistential方法拿到type。如果传入的passedType不为空,就将传入的passedType赋值给type
call
来到
unwrapExistential方法定义,通过getDynamicType方法获取当前的动态类型。通过内部方法调用,最终发现获取类型的本质是依赖Metadata元数据。
unwrapExistential
找到
Metadata,由于类结构的Metadata过于复杂,我们下面以结构体TargetStructMetadata为例,它继承于TargetValueMetadata
TargetStructMetadata
来到
TargetValueMetadata定义,它继承于TargetMetadata,除了继承下来的kind属性,还有一个ConstTargetMetadataPointer类型的description属性,而description属性记录的就是有关元数据的描述。ConstTargetMetadataPointer实际是一个泛型,需要重点分析的是TargetValueTypeDescription
TargetValueMetadata
下面介绍元数据的描述,进入
TargetValueTypeDescription定义,它继承于TargetTypeContextDescription
TargetValueTypeDescription
进入
TargetTypeContextDescription定义,里面有一个TargetRelativeDirectPointer类型的Name属性,用于记录当前类型的名称
TargetTypeContextDescription
进入
TargetRelativeDirectPointer定义,它是RelativeDirectPointer类型的别名
TargetRelativeDirectPointer
进入
RelativeDirectPointer定义,它是一个模板类,继承于RelativeDirectPointerImpl,传入类型T、Nullable为true、Offset为int32_t
RelativeDirectPointer
进入
RelativeDirectPointerImpl定义,它有一个int32_t类型的RelativeOffset属性
RelativeDirectPointerImpl
还包含
ValueTy、PointerTy,其中ValueTy代表T的类型,PointerTy代表T的指针类型
ValueTy、PointerTy
回到
RelativeDirectPointer定义,获取值ValueTy、获取指针PointerTy调用的都是this->get()方法
获取ValueTy、PointerTy
进入
get方法,又回到RelativeDirectPointerImpl定义,方法内调用applyRelativeOffset方法
get
进入
applyRelativeOffset方法,返回base + extendOffset,base是当前指针地址,extendOffset是相对地址(偏移地址),最终相加得到偏移后的地址存储的是类型
applyRelativeOffset
回到
call函数,可以看到很多依赖都是基于call函数。比如获取Class数据时,通过callClass方法得到一个ClassImpl。如果是元组类型,会得到一个TupleImpl。如果是结构体,会得到StructImpl。
call
以结构体为例,进入
StructImpl定义,继承于ReflectionMirrorImpl。其中isReflectable方法,也是通过Metadata获取getDescriotion,找到isReflectable判断是否支持反射
isReflectable
count方法,同样通过Metadata获取getDescriotion,找到NumFields获取属性总数
count
subscipt方法,获取属性名称和属性值。通过getFieldOffests()[i]得到一个属性的偏移值。通过getFiledAt方法拿到属性名称,bytes是当前value地址,加上fieldOffset偏移地址,就是属性的值
subscipt
进入
getFiledAt方法,通过baseDesc的Fields.get()获取fields,将fields赋值给FieldDescriptor类型的descriptor,通过descriptor.geyFields[index]获取一个属性field,最终通过field.getFieldName()拿到属性值的名称
getFiledAt
Fields也是一个相对指针的存储,类型是FieldDescriptor
FieldDescriptor
getFieldName方法调用的是FieldName.get(),在FieldRecord类里
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):当前thisadvanced:移动步长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

传入类对象
传入类的类型
Mirror
init
Children
Mirror.Child
return
上述代码中,将
throw
throws
try?
try!
try
lg_add
swift_lg_add
Mirror
init
internalReflecting
_getNormalizedType
swift_reflectionMirror_normalizedType
call
unwrapExistential
TargetStructMetadata
TargetValueMetadata
TargetValueTypeDescription
TargetTypeContextDescription
TargetRelativeDirectPointer
RelativeDirectPointer
RelativeDirectPointerImpl
ValueTy、PointerTy
获取ValueTy、PointerTy
get
applyRelativeOffset
call
isReflectable
count
subscipt
getFiledAt
FieldDescriptor
FieldRecord










网友评论