Mirror是Swift中的反射机制,反射就是可以动态的获取类型以及成员信息,同时也可以在运行时动态的调用方法和属性等。
1. Mirror 简介
Mirror是Swift中的反射机制的实现,它的本质是一个结构体。
创建 Mirror 最简单的方式就是使用 reflecting 构造器:
public init(reflecting subject: Any)
正如你所见,对象的类型是 Any。这是 Swift 中最通用的类型。Swift 中的任何东西至少都是 Any类型的。这样一来 mirror 就可以兼容 struct, class, enum, Tuple, Array, Dictionary, set 等。
下面列举了 Mirror 可用的属性 / 方法:
displayStyle
- public enum DisplayStyle {
- case Struct
- case Class
- case Enum
- case Tuple
- case Optional
- case Collection
- case Dictionary
- case Set
- }
它会返回 DisplayStyle enum 的其中一种情况。如果你想要对某种不支持的类型进行反射,你会得到一个空的 Optional 值。
例如:
正如之前我们知道的,反射只要求对象是 Any 类型,而且Swift 标准库中还有很多类型为 Any 的东西没有被列举在上面的 DisplayStyle enum 中。如果试图反射它们中间的某一个又会发生什么呢?比如 closure。
- let closure = { (a: Int) -> Int in return a * 2 }
- let aMirror = Mirror(reflecting: closure)
- print("\(aMirror)----------\(aMirror.displayStyle)")
- //Mirror for (Int) -> Int----------nil
children
这会返回一个包含了对象所有的子节点的 AnyForwardCollection
- public class Store {
- let storesToDisk: Bool = true
- }
- public class BookmarkStore: Store {
- let itemCount: Int = 10
- }
- public struct Bookmark {
- enum Group {
- case Tech
- case News
- }
- private let store = {
- return BookmarkStore()
- }()
- let title: String?
- let url: NSURL
- let keywords: [String]
- let group: Group
- }
- let aBookmark = Bookmark(title: "Appventure", url: NSURL(string: "appventure.me")!, keywords: ["Swift", "iOS", "OSX"], group: .Tech)
-
- let aMirror = Mirror(reflecting: aBookmark)
- for case let (label?, value) in aMirror.children {
- print (label, value)
- }
- //输出:
- //store TestDemo3.ViewController.BookmarkStore
- //title Optional("Appventure")
- //url appventure.me
- //keywords ["Swift", "iOS", "OSX"]
- //group Tech
SubjectType
这是对象的类型:
- print(aMirror.subjectType)
- //输出 : Bookmark
- print(Mirror(reflecting: 5).subjectType)
- //输出 : Int
- print(Mirror(reflecting: "test").subjectType)
- //输出 : String
- print(Mirror(reflecting: NSNull()).subjectType)
- //输出 : NSNull
SuperclassMirror
这是我们对象父类的 mirror。如果这个对象不是一个类,它会是一个空的 Optional 值。如果对象的类型是基于类的,你会得到一个新的 Mirror:
- // 试试 struct
- print(Mirror(reflecting: aBookmark).superclassMirror)
- // 输出: nil
- // 试试 class
- print(Mirror(reflecting: aBookmark.store).superclassMirror)
- // 输出: Optional(Mirror for Store)
2.反射 Mirror 使用
1.Mirror 转换对象为字典
- struct Person {
- var name: String = "YDW"
- var isMale: Bool = true
- var birthday: Date = Date()
- }
-
- class Animal: NSObject {
- private var eat: String = "吃"
- var age: Int = 0
- var optionValue: String?
- }
-
- class Cat: Animal {
- var like: [String] = ["mouse", "fish"]
- var master = Person()
- }
- func mapDic(mirror: Mirror) -> [String: Any] {
- var dic: [String: Any] = [:]
- for child in mirror.children {
- // 如果没有label就会被抛弃
- if let label = child.label {
- let propertyMirror = Mirror(reflecting: child.value)
- 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 objc = Cat()
- let mirror = Mirror(reflecting: objc)
- let mirrorDic = mapDic(mirror: mirror)
- print(mirrorDic)
-
- // 打印结果
- ["master": TestDemo3.ViewController.Person(name: "YDW", isMale: true, birthday: 2022-07-15 03:51:59 +0000), "eat": "吃", "optionValue": nil, "age": 0, "like": ["mouse", "fish"]]
- // 外部参数定义
- var params = (title: "name", comment: "Mirror")
-
- // 网络层统一转换为字典,进行网路请求
- let paramsDic = mapDic(mirror: Mirror(reflecting: params))
- print(paramsDic)
-
- // 打印结果
- ["title": "name", "comment": "Mirror"]
- // 外部参数定义
- var params = ("name","Mirror")
-
- // 网络层统一转换为字典,进行网路请求
- let paramsDic = mapDic(mirror: Mirror(reflecting: params))
- print(paramsDic)
- // 打印
- [".1": "Mirror", ".0": "name"]
- JSON 解析
- class YDWTeacher {
- var age = 18
- var name = "YDW"
- }
- // JSON解析
- 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 = YDWTeacher()
- print(test(t))
["name": "YDW","age": 18]
- // 定义JSON解析协议
- protocol CustomJSONMap {
- func jsonMap() -> Any
- }
-
- // 提供默认实现
- extension CustomJSONMap{
- func jsonMap() -> Any{
- let mirror = Mirror(reflecting: self)
- // 递归终止条件
- guard !mirror.children.isEmpty else {
- return self
- }
- // 字典,用于存储json数据
- 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 YDWTeacher: CustomJSONMap {
- var age = 18
- var name = "YDW"
- }
-
- // 使用
- var t = YDWTeacher()
- print(t.jsonMap())
- 当前-18-没有遵守协议
- 当前-YDW-没有遵守协议
- // Int、String遵守协议
- extension Int: CustomJSONMap{}
- extension String: CustomJSONMap{}
-
- // 打印结果
- ["name": "YDW", "age": 18]
3.错误处理
public protocol Error { }
- // 定义错误类型
- enum JSONMapError: Error{
- case emptyKey
- case notConformProtocol
- }
- extension CustomJSONMap {
- func jsonMap() throws -> Any {
- let mirror = Mirror(reflecting: self)
- // 递归终止条件
- guard !mirror.children.isEmpty else {
- return self
- }
- // 字典,用于存储json数据
- 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
- }
- }
- // 使用时需要加上try
- var t = YDWTeacher()
- do{
- _ = try t.jsonMap()
- }catch JSONMapError.emptyKey{
- print("???emptyKey????")
- }catch JSONMapError.notConformProtocol{
- print("???notConformProtocol?????")
- }catch{
- }
4.获取类型,属性个数及其值
- class YDWUser {
- var name: String = ""
- var nickName: String?
- var age: Int?
- var emails: [String]?
- }
- // 创建一个User实例对象
- let user = YDWUser()
- user.name = "DW"
- user.age = 18
- user.emails = ["12345@qq.com", "56789@qq.com"]
-
- // 将user对象进行反射
- let hMirror = Mirror(reflecting: user)
-
- print("对象类型:\(hMirror.subjectType)")
- print("对象子元素个数:\(hMirror.children.count)")
-
- print("--- 对象子元素的属性名和属性值分别如下 ---")
- for case let (label?, value) in hMirror.children {
- print("属性:\(label) 值:\(value)")
- }
- 对象类型:YDWUser
- 对象子元素个数:4
- --- 对象子元素的属性名和属性值分别如下 ---
- 属性:name 值:DW
- 属性:nickName 值:nil
- 属性:age 值:Optional(18)
- 属性:emails 值:Optional(["12345@qq.com", "56789@qq.com"])
5.通过属性名(字符串)获取对应的属性值,并对值做类型判断(包括是否为空)
- // 根据属性名字符串获取属性值
- func getValueByKey(obj: AnyObject, key: String) -> Any {
- let hMirror = Mirror (reflecting: obj)
- for case let (label?, value) in hMirror.children {
- if label == key {
- return unwrap(any: value)
- }
- }
- return NSNull ()
- }
-
- // 将可选类型(Optional)拆包
- func unwrap(any: Any ) -> Any {
- let mi = Mirror (reflecting: any)
- if mi.displayStyle! != .optional {
- return any
- }
-
- if mi.children.count == 0 {
- return any
- }
- let (_, some) = mi.children.first!
- return some
- }
- // 创建一个User实例对象
- let user = YDWUser()
- user.name = "DW"
- user.age = 18
- user.emails = ["12345@qq.com", "56789@qq.com"]
-
- // 通过属性名字符串获取对应的值
- let name = getValueByKey(obj: user, key: "name")
- let nickName = getValueByKey(obj: user, key: "nickName")
- let age = getValueByKey(obj: user, key: "age")
- let emails = getValueByKey(obj: user, key: "emails")
- let tel = getValueByKey(obj: user, key: "tel")
- print(name, nickName, age, emails, tel)
-
- // 对于获取到的值进行类型判断
- if name is NSNull {
- print("name这个属性不存在")
- } else if (name as? AnyObject) == nil {
- print("name这个属性是个可选类型,且为nil")
- } else if name is String {
- print("name这个属性String类型,其值为:\(name)")
- }
- if tel is NSNull {
- print("tel这个属性不存在")
- } else if (tel as? AnyObject) == nil {
- print("tel这个属性是个可选类型,且为nil")
- } else if tel is String {
- print("tel这个属性String类型,其值为:\(tel)")
- }
- DW nil 18 ["12345@qq.com", "56789@qq.com"] <null>
- name这个属性String类型,其值为:DW
- tel这个属性不存在
3.Swift动态性
静态派发
在编译期的时候,编译器就知道要为某个方法调用某种实现
动态派发
对于每个方法的调用,编译器必须在方法列表中查找执行方法的实现,比如在运行时判断是选择父类的实现,还是子类的实现。对于对象的内存都是在运行时分配的,因此只能在运行时执行检查
编译型语言的函数派发方式
直接派发
static声明的方法final声明的所有方法,使用final声明的类里面的所有方法private声明的方法和属性,会隐式final声明struct和enum中的方法extension中没有使用@objc修饰的实例方法函数表派发
Protocol默认使用的是函数表派发,协议可以为struct提供多态的支持witness table(C++叫做virtual table)消息派发
dynamic修饰的方法是消息派发@objc修饰方法,只是把方法暴露给objc,是函数表派发例子
- //举例说明:
- class CustomView: UIView {
- /// static修饰:直接派发
- static func staticMehod() {}
- /// private: 直接派发
- private func privateMethod() {}
- /// final修饰:直接派发
- final func finalMethod() {}
- /// 直接派发
- static func staticMethod() {}
- /// 普通的实例方法 函数表派发
- func commonMethod() {}
- /// @objc修饰 函数表派发
- @objc func method1() {}
- /// dynamic修饰: 消息派发
- @objc dynamic
- func method2() {}
- /// 重写了OC的方法: 消息派发
- override func layoutSubviews() {
- super.layoutSubviews()
- }
- }