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<Child>。这些子节点不单单限于 Array 或者 Dictionary 中的条目。诸如 struct 或者 class 中所有的属性也是由 AnyForwardCollection<Child> 这个属性返回的子节点。AnyForwardCollection 协议意味着这是一个支持遍历的 Collection 类型。
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()
}
}