前言 Mirror是Swift中的反射机制,对于C#和Java开发人员来说,应该很熟悉反射这个概念。反射就是可以动态的获取类型以及成员信息,同时也可以在运行时动态的调用方法和属性等。
对于iOS开发人员来说,入门时使用的Objective-C是很少强调反射概念的,因为OC的Runtime要比其他语言的反射强大的多。
1. Mirror 简介
Mirror
是Swift
中的反射机制的实现,它的本质是一个结构体。其部分源码(Swift 5.3.1
)如下:
public struct Mirror {
/// A suggestion of how a mirror's subject is to be interpreted.
///
/// Playgrounds and the debugger will show a representation similar
/// to the one used for instances of the kind indicated by the
/// `DisplayStyle` case name when the mirror is used for display.
public enum DisplayStyle {
case `struct`, `class`, `enum`, tuple, optional, collection
case dictionary, `set`
}
/// The static type of the subject being reflected.
///
/// This type may differ from the subject's dynamic type when this mirror
/// is the `superclassMirror` of another mirror.
public let subjectType: Any.Type
/// A collection of `Child` elements describing the structure of the
/// reflected subject.
public let children: Children
/// A suggested display style for the reflected subject.
public let displayStyle: DisplayStyle?
/// A mirror of the subject's superclass, if one exists.
public var superclassMirror: Mirror? {
return _makeSuperclassMirror()
}
}
subjectType
:表示类型,被反射主体的类型children
:子元素集合displayStyle
:显示类型,基本类型为 nil 枚举值:struct, class, enum, tuple, optional, collection, dictionary, set
superclassMirror
:父类反射, 没有父类为 nil
除了这些属性还有一些初始化方法,我们最常用的就是初始化方法就是:
代码语言:javascript复制 /// Creates a mirror that reflects on the given instance.
///
/// If the dynamic type of `subject` conforms to `CustomReflectable`, the
/// resulting mirror is determined by its `customMirror` property.
/// Otherwise, the result is generated by the language.
///
/// If the dynamic type of `subject` has value semantics, subsequent
/// mutations of `subject` will not observable in `Mirror`. In general,
/// though, the observability of mutations is unspecified.
///
/// - Parameter subject: The instance for which to create a mirror.
public init(reflecting subject: Any) {
if case let customized as CustomReflectable = subject {
self = customized.customMirror
} else {
self = Mirror(internalReflecting: subject)
}
}
根据源码我们还可以看到能够使用customMirror,这个没太研究,在源码中可以看到很多CustomMirror的身影,感兴趣的可以去研究研究。对于非customMirror的统一使用Mirror(internalReflecting:)进行初始化。
关于customMirror的补充,摘抄自 swiftGG Mirror 的工作原理。
Mirror允许类型用遵循 CustomReflectable 协议的方式提供一个自定义的表示方式。这给那些想表示得比内建形式更友好的类型提供一种有效的方法。比如 Array 类型遵守 CustomReflectable 协议并且暴露其中的元素为无标签的 Children。Dictionary 使用这种方法暴露其中的键值对为带标签的 Children。
2. Mirror的简单使用
▐ 2.1 基本使用
这里我们通过使用Mirror
打印对象的属性名称和属性值。
class Person {
var name: String = "xiaohei"
var age: Int = 18
var height = 1.85
}
var p = Person()
var mirror = Mirror(reflecting: p.self)
print("对象类型:(mirror.subjectType)")
print("对象属性个数:(mirror.children.count)")
print("对象的属性及属性值")
for child in mirror.children {
print("(child.label!)---(child.value)")
}
打印结果:
我们可以看到,属性名称和值都已经正常打印。
▐ 2.2 将对象转换为字典
首先我们来体验一下将对象转换为字典。
代码语言:javascript复制class Animal {
var name: String?
var color: String?
private var birthday: Date = Date(timeIntervalSince1970: 0)
}
class Cat: Animal {
var master = "小黑"
var like: [String] = ["mouse", "fish"]
override init() {
super.init()
color = "黄色"
}
}
func mapDic(mirror: Mirror) -> [String: Any] {
var dic: [String: Any] = [:]
for child in mirror.children {
// 如果没有labe就会被抛弃
if let label = child.label {
let propertyMirror = Mirror(reflecting: child.value)
print(propertyMirror)
dic[label] = child.value
}
}
// 添加父类属性
if let superMirror = mirror.superclassMirror {
let superDic = mapDic(mirror: superMirror)
for p in superDic {
dic[p.key] = p.value
}
}
return dic
}
// Mirror使用
let cat = Cat()
cat.name = "大橘为重"
let mirror = Mirror(reflecting: cat)
let mirrorDic = mapDic(mirror: mirror)
print(mirrorDic)
打印结果:
通过打印结果我们可以看到,对于一些基本类型,已经可选类型的数据都已经转换为字典值,对于私有属性也可以完成转换。如果里面包含类则还需要进行递归处理。
▐ 2.3 转 JSON
注:这里并没有真正的转换成json字符串,还是只转换成了字典,重要在思想,如果需要转换成json还需要很多优化,以及特殊字符串的考量。
其实提到反射我们想到最多的应该就是JSON
了,这里我们利用Mirror
的特性,将对象转换成字典,对基本类型和类做了相应的处理,体会一下转json的思路。
首先我们定义一个Person
对象,代码如下:
struct Person {
var name: String = "xiaohei"
var age: Int = 18
var isMale: Bool = true
var address: Address? = Address(street: "xizhimen North")
var height = 1.85
var like: Array = ["eat", "sleep", "play"]
var weight: Float = 75.0
var some: Int?
}
struct Address {
var street: String
}
// 创建一个Person对象
let p = Person()
为了通用性,我们可以编写一个协议,用来为所有类型提供转换的方法,只需要遵守该协议就可以使用协议中的方法。
代码语言:javascript复制protocol CustomJSONProtocol {
func toJSON() throws -> Any?
}
协议的实现过程中会有些错误,我们也简单的定义个枚举,方便处理。为了更加详细的描述错误信息,我们添加了错误描述和错误 code。
代码语言:javascript复制// 转 json 时的错误类型
enum JSONMapError: Error{
case emptyKey
case notConformProtocol
}
// 错误描述
extension JSONMapError: LocalizedError{
var errorDescription: String?{
switch self {
case .emptyKey:
return "key 为空"
case .notConformProtocol:
return "没遵守协议"
}
}
}
// errorcode
extension JSONMapError: CustomNSError{
var errorCode: Int{
switch self {
case .emptyKey:
return 100
case .notConformProtocol:
return 101
}
}
}
协议实现的代码:
代码语言:javascript复制extension CustomJSONProtocol {
func toJSON() throws -> Any? {
//创建 Mirror 类型
let mirror = Mirror(reflecting: self)
// 如果没有属性,比如一般类型String、Int等,直接返回自己
guard !mirror.children.isEmpty else { return self }
var result: [String:Any] = [:]
// 遍历
for children in mirror.children {
if let value = children.value as? CustomJSONProtocol{
if let key = children.label {
print(key)
result[key] = try value.toJSON()
} else {
throw JSONMapError.emptyKey
}
} else {
throw JSONMapError.notConformProtocol
}
}
return result
}
}
将用到的类型都遵守协议
代码语言:javascript复制//将一般类型都遵从 CustomJSONProtocol 协议
extension Person: CustomJSONProtocol {}
extension String: CustomJSONProtocol {}
extension Int: CustomJSONProtocol {}
extension Bool: CustomJSONProtocol {}
extension Double: CustomJSONProtocol {}
extension Float: CustomJSONProtocol {}
extension Address: CustomJSONProtocol {}
// 数组需要单独处理,要不然就会报错emptyKey
extension Array: CustomJSONProtocol {
func toJSON() throws -> Any? {
return self
}
}
//Optionai 需要特别对待,原因是如果直接返回,则会是 .Some: [...]
extension Optional: CustomJSONProtocol {
func toJSON() throws -> Any? {
if let x = self {
if let value = x as? CustomJSONProtocol {
return try value.toJSON()
}
throw JSONMapError.notConformProtocol
}
return nil
}
}
最后我们打印一下:
代码语言:javascript复制do {
print(try p.toJSON()!)
} catch {
print(error.localizedDescription)
print((error as? JSONMapError)?.errorCode)
}
打印结果:
我们看到,对于some
这空值,并没有存储到字典中,因为swift
中的字典对于空值是删除的意思。
如果想将其转换成json
还需修改"[]"为"{}",这个对于数组和对象还不好区分,另外对于json
字符串内的一些value
也有可能是应一串json
还需要添加转义字符等。
所以总的来说,思路是这样的,要想真正的做成通用的转json
的方案还需要很多的优化,比如说,我们不可能将所有的基本类型都去遵守一个协议,这时候我们也可以考虑使用泛型去作为方法的参数。
3. Mirror 源码解析
源码版本Swift 5.3.1
在本章节我们将分析Mirror
的部分源码,查看其底层实现,最后通过Swift
代码使用内存重绑定的形式,仿写一下Mirror
,来更好的探索Mirror
的原理,理解Mirror
的思想。
我们知道Swift
是一门静态语言,那么在底层是如何实现的获取对应的属性值的呢?又或者说Swift
的反射特性是如何实现的呢?下面我们通过对Mirror
底层源码的探索来寻找答案。
▐ 3.1 代码结构
Mirror
的实现是由一部分Swift
代码加上另一部分C
代码。Swift
代码实现在ReflectionMirror.swift
文件中,C
代码实现在ReflectionMirror.mm
文件中。Swift
更适合用在实现更Swift
的接口,但是在Swift
中不能直接访问C
的类。这里使用了@_silgen_name
来实现Swift
调用C
中的方法。举个例子:
@_silgen_name("swift_reflectionMirror_count")
internal func _getChildCount<T>(_: T, type: Any.Type) -> Int
@_silgen_name
修饰符会通知Swift
编译器将这个函数映射成swift_reflectionMirror_count
符号,而不是Swift
通常对应到的_getChildCount
方法名修饰。需要注意的是,最前面的下划线表示这个修饰是被保留在标准库中的。在C
这边,这个函数是这样的。
// func _getChildCount<T>(_: T, type: Any.Type) -> Int
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
intptr_t swift_reflectionMirror_count(OpaqueValue *value,
const Metadata *type,
const Metadata *T) {
return call(value, T, type, [](ReflectionMirrorImpl *impl) {
return impl->count();
});
}
SWIFT_CC(swift)
会告诉编译器这个函数使用的是Swift
的调用约定,而不是C/C
的,SWIFT_RUNTIME_STDLIB_API
标记这个函数,在Swift
侧的一部分接口中,而且它还有标记为extern "C"
的作用,从而避免C
的方法名修饰,并确保它在Swift
侧会有预期的符号。同时C
的参数会去特意匹配在Swift中声明的函数调用。当Swift
调用_getChildCount
时,C
会用包含Swift
值指针的value
,包含类型参数type
,包含类型响应的泛型<T>
的T
的函数参数来调用此函数。
简单的说就是使用@_silgen_name("xxx")
修饰符修饰的Swift
方法会调用括号中的xxx
的符号,不管是C
的还是C
的都可以。
Mirror
的在Swift
和C
之间的全部接口由以下函数组成:
@_silgen_name("swift_isClassType")
internal func _isClassType(_: Any.Type) -> Bool
@_silgen_name("swift_getMetadataKind")
internal func _metadataKind(_: Any.Type) -> UInt
@_silgen_name("swift_reflectionMirror_normalizedType")
internal func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
@_silgen_name("swift_reflectionMirror_count")
internal func _getChildCount<T>(_: T, type: Any.Type) -> Int
@_silgen_name("swift_reflectionMirror_recursiveCount")
internal func _getRecursiveChildCount(_: Any.Type) -> Int
@_silgen_name("swift_reflectionMirror_recursiveChildMetadata")
internal func _getChildMetadata(
_: Any.Type,
index: Int,
outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
) -> Any.Type
@_silgen_name("swift_reflectionMirror_recursiveChildOffset")
internal func _getChildOffset(
_: Any.Type,
index: Int
) -> Int
internal typealias NameFreeFunc = @convention(c) (UnsafePointer<CChar>?) -> Void
@_silgen_name("swift_reflectionMirror_subscript")
internal func _getChild<T>(
of: T,
type: Any.Type,
index: Int,
outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
) -> Any
// Returns 'c' (class), 'e' (enum), 's' (struct), 't' (tuple), or '