swift 進(jìn)階:反射機(jī)制Mirror & 錯(cuò)誤處理

swift 進(jìn)階之路:學(xué)習(xí)大綱

內(nèi)容

    1. 反射機(jī)制Mirror
    1. 錯(cuò)誤處理
    1. Mirror源碼解析

1仁讨、 反射Mirror

反射:是指可以動(dòng)態(tài)獲取類型晒哄、成員信息比然,在運(yùn)行時(shí)可以調(diào)用方法、屬性等行為的特性指蚁,

  • 在上面的分析中,我們已經(jīng)知道,對(duì)于一個(gè)純swift類來說恬惯,并不支持直接像OC runtime那樣的操作

  • 但是swift標(biāo)準(zhǔn)庫依舊提供了反射機(jī)制蜻牢,用來訪問成員信息烤咧,即Mirror

一般使用

class CJLTeacher: NSObject {
    var age: Int = 18
}
let mirror = Mirror(reflecting: CJLTeacher.self)
for pro in mirror.children{
    print("\(pro.label): \(pro.value)")
}

  • 運(yùn)行上面代碼,發(fā)現(xiàn)沒有任何打印抢呆,為什么煮嫌?是因?yàn)?code>Mirror中傳入的參數(shù)不對(duì),應(yīng)該是傳入實(shí)例對(duì)象抱虐,修改如下
class CJLTeacher: NSObject {
    var age: Int = 18
}
var t = CJLTeacher()
//傳入t也可以
let mirror = Mirror(reflecting: t.self)
for pro in mirror.children{
    print("\(pro.label): \(pro.value)")
}

image

查看Mirror定義

  • 進(jìn)入Mirror初始化方法昌阿,發(fā)現(xiàn)傳入的類型是Any,則可以直接傳t
public init(reflecting subject: Any)

  • 進(jìn)入children
public let children: Mirror.Children
??
//進(jìn)入Children恳邀,發(fā)現(xiàn)是一個(gè)AnyCollection懦冰,接收一個(gè)泛型
public typealias Children = AnyCollection<Mirror.Child>
??
//進(jìn)入Child,發(fā)現(xiàn)是一個(gè)元組類型轩娶,由可選的標(biāo)簽和值構(gòu)成儿奶,
public typealias Child = (label: String?, value: Any)

這也是為什么能夠通過labelvalue打印的原因鳄抒。即可以在編譯時(shí)期且不用知道任何類型信息情況下闯捎,在Child的值上用Mirror遍歷整個(gè)對(duì)象的層級(jí)視圖

JSON解析

根據(jù)Mirror的這個(gè)特性,我們思考下许溅,可以通過Mirror做什么瓤鼻?首先想到的是JSON解析,如下所示贤重,我們定義了一個(gè)CJLTeacher類茬祷,然后通過一個(gè)test方法來解析t

class CJLTeacher {
    var age = 18
    var name = "CJL"
}

<!--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 {
            //遞歸調(diào)用
            keyValue[keyName] = test(children.value)
        }else{
            print("children.label 為空")
        }
    }
    return keyValue
}

<!--使用-->
var t = CJLTeacher()
print(test(t))

代碼的打印結(jié)果如下谣膳,打印出了key-value

image

JSON解析封裝
如果我們想大規(guī)模的使用上述的JSON解析溯街,上面只是針對(duì)CJLTeacher的JSON解析克懊,所以统捶,為了方便其他類使用,可以將JSON解析抽取成一個(gè)協(xié)議沃粗,然后提供一個(gè)默認(rèn)實(shí)現(xiàn)粥惧,讓類遵守協(xié)議

//1、定義一個(gè)JSON解析協(xié)議
protocol CustomJSONMap {
    func jsonMap() -> Any
}
//2最盅、提供一個(gè)默認(rèn)實(shí)現(xiàn)
extension CustomJSONMap{
    func jsonMap() -> Any{
        let mirror = Mirror(reflecting: self)
        //遞歸終止條件
        guard !mirror.children.isEmpty else {
            return self
        }
        //字典突雪,用于存儲(chǔ)json數(shù)據(jù)
        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("當(dāng)前-\(children.value)-沒有遵守協(xié)議")
            }
        }
        return keyValue
    }
}

//3、讓類遵守協(xié)議(注意:類中屬性的類型也需要遵守協(xié)議涡贱,否則無法解析)
class CJLTeacher: CustomJSONMap {
    var age = 18
    var name = "CJL"
}

//使用
var t = CJLTeacher()
print(t.jsonMap())

【問題】:運(yùn)行代碼發(fā)現(xiàn)咏删,并不是我們想要的結(jié)果,原因是因?yàn)镃JLTeacher中屬性的類型也需要遵守CustomJSONMap協(xié)議

image

【修改】:所以在原有基礎(chǔ)上增加以下代碼

//Int、String遵守協(xié)議
extension Int: CustomJSONMap{}
extension String: CustomJSONMap{}

修改后的運(yùn)行結(jié)果如下

image

2问词、 錯(cuò)誤處理

為了讓我們自己封裝的JSON解析更好用督函,除了對(duì)正常返回的處理,還需要對(duì)其中的錯(cuò)誤進(jìn)行處理戏售,在上面的封裝中侨核,我們目前采用的是print打印的,這樣并不規(guī)范灌灾,也不好維護(hù)及管理搓译。那么如何在swift中正確的表達(dá)錯(cuò)誤呢?

首先锋喜,Swift中些己,提供了Error協(xié)議標(biāo)識(shí)當(dāng)前應(yīng)用程序發(fā)生錯(cuò)誤的情況,其中Error的定義如下

public protocol Error {
}

Error是一個(gè)空協(xié)議嘿般,其中沒有任何實(shí)現(xiàn)段标,這也就意味著你可以遵守該協(xié)議,然后自定義錯(cuò)誤類型炉奴。所以不管是我們的struct逼庞、Class、enum瞻赶,我們都可以遵循這個(gè)Error來表示一個(gè)錯(cuò)誤

所以接下來赛糟,對(duì)我們上面封裝的JSON解析修改其中的錯(cuò)誤處理

  • 定義一個(gè)JSONMapError錯(cuò)誤枚舉,將默認(rèn)實(shí)現(xiàn)的print替換成枚舉類型
//定義錯(cuò)誤類型
enum JSONMapError: Error{
    case emptyKey
    case notConformProtocol
}

//1砸逊、定義一個(gè)JSON解析協(xié)議
protocol CustomJSONMap {
    func jsonMap() -> Any
}
//2璧南、提供一個(gè)默認(rèn)實(shí)現(xiàn)
extension CustomJSONMap{
    func jsonMap() -> Any{
        let mirror = Mirror(reflecting: self)
        //遞歸終止條件
        guard !mirror.children.isEmpty else {
            return self
        }
        //字典,用于存儲(chǔ)json數(shù)據(jù)
        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{
                    return JSONMapError.emptyKey
                }
            }else{
                return JSONMapError.notConformProtocol
            }
        }
        return keyValue
    }
}

  • 但是這里有一個(gè)問題师逸,jsonMap方法的返回值是Any司倚,我們無法區(qū)分返回的是錯(cuò)誤還是json數(shù)據(jù),那么該如何區(qū)分呢?即如何拋出錯(cuò)誤呢动知?在這里可以通過throw關(guān)鍵字(即將Demo中原本return的錯(cuò)誤皿伺,改成throw拋出)
//2、提供一個(gè)默認(rèn)實(shí)現(xiàn)
extension CustomJSONMap{
    func jsonMap() -> Any{
        let mirror = Mirror(reflecting: self)
        //遞歸終止條件
        guard !mirror.children.isEmpty else {
            return self
        }
        //字典盒粮,用于存儲(chǔ)json數(shù)據(jù)
        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{
                    throw JSONMapError.emptyKey
                }
            }else{
                throw JSONMapError.notConformProtocol
            }
        }
        return keyValue
    }
}

  • 發(fā)現(xiàn)改成throw拋出錯(cuò)誤后心傀,編譯器提示有錯(cuò),其原因是因?yàn)榉椒ú]有聲明成throws

    image
  • 所以還需要在方法的返回值箭頭前增加throws(表示將方法中錯(cuò)誤拋出)拆讯,修改后的默認(rèn)實(shí)現(xiàn)代碼如下所示

//1、定義一個(gè)JSON解析協(xié)議
protocol CustomJSONMap {
    func jsonMap() throws-> Any
}
//2养叛、提供一個(gè)默認(rèn)實(shí)現(xiàn)
extension CustomJSONMap{
    func jsonMap() throws -> Any{
        let mirror = Mirror(reflecting: self)
        //遞歸終止條件
        guard !mirror.children.isEmpty else {
            return self
        }
        //字典种呐,用于存儲(chǔ)json數(shù)據(jù)
        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{
                    throw JSONMapError.emptyKey
                }
            }else{
                throw JSONMapError.notConformProtocol
            }
        }
        return keyValue
    }
}

  • 由于我們?cè)趈sonMap方法中遞歸調(diào)用了自己,所以還需要在遞歸調(diào)用前增加 try 關(guān)鍵字
//2弃甥、提供一個(gè)默認(rèn)實(shí)現(xiàn)
extension CustomJSONMap{
    func jsonMap() throws -> Any{
        let mirror = Mirror(reflecting: self)
        //遞歸終止條件
        guard !mirror.children.isEmpty else {
            return self
        }
        //字典爽室,用于存儲(chǔ)json數(shù)據(jù)
        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
    }
}

<!--使用時(shí)需要加上try-->
var t = CJLTeacher()
print(try t.jsonMap())

到此,一個(gè)完整的錯(cuò)誤表達(dá)方式就完成了

swift中錯(cuò)誤處理的方式

swift中錯(cuò)誤處理的方式主要有以下兩種:

  • 【方式一】:使用try關(guān)鍵字淆攻,是最簡(jiǎn)便的阔墩,即甩鍋,將這個(gè)拋出給別人(向上拋出瓶珊,拋給上層函數(shù))啸箫。但是在使用時(shí),需要注意以下兩點(diǎn):
    • try? 返回一個(gè)可選類型伞芹,只有兩種結(jié)果:

      • 要么成功忘苛,返回具體的字典值

      • 要么錯(cuò)誤,但并不關(guān)心是哪種錯(cuò)誤唱较,統(tǒng)一返回nil

    • try! 表示你對(duì)這段代碼有絕對(duì)的自信扎唾,這行代碼絕對(duì)不會(huì)發(fā)生錯(cuò)誤

  • 【方式二】:使用do...catch

【方式一】try

通過try來處理JSON解析的錯(cuò)誤

//CJLTeacher中定義一個(gè)height屬性,并未遵守協(xié)議
class CJLTeacher: CustomJSONMap {
    var age = 18
    var name = "CJL"
    var height = 1.85
}

/*****1南缓、try? 示例*****/
var t = CJLTeacher()
print(try? t.jsonMap())

/*****打印結(jié)果*****/
nil

/*****2胸遇、try! 示例*****/
var t = CJLTeacher()
print(try! t.jsonMap())

/*****打印結(jié)果*****/
Fatal error: 'try!' expression unexpectedly raised an error: _5_MirrorAndError.JSONMapError.notConformProtocol: file _5_MirrorAndError/main.swift, line 90
2020-12-20 18:27:28.305112+0800 05-MirrorAndError[18642:1408258] Fatal error: 'try!' expression unexpectedly raised an error: _5_MirrorAndError.JSONMapError.notConformProtocol: file _5_MirrorAndError/main.swift, line 90

<!--3、如果直接使用try汉形,是向上拋出-->
var t = CJLTeacher()
try t.jsonMap()

/*****打印結(jié)果*****/
Fatal error: Error raised at top level: _5_MirrorAndError.JSONMapError.notConformProtocol: file Swift/ErrorType.swift, line 200
2020-12-20 18:31:24.837476+0800 05-MirrorAndError[18662:1410970] Fatal error: Error raised at top level: _5_MirrorAndError.JSONMapError.notConformProtocol: file Swift/ErrorType.swift, line 200

從上面可以知道纸镊,錯(cuò)誤是向上拋出的,即拋給了上層函數(shù)获雕,如果上層函數(shù)也不處理薄腻,則直接拋給main,main沒有辦法處理届案,則直接報(bào)錯(cuò)庵楷,例如下面的例子

//使用
var t = CJLTeacher()

func test() throws -> Any{
    try t.jsonMap()
}
try test()

其運(yùn)行結(jié)果如下

image

方式二:do-catch

通過do-catch來處理JSON解析的錯(cuò)誤

var t = CJLTeacher()
do{
    try t.jsonMap()
}catch{
    print(error)
}

運(yùn)行結(jié)果如下

image

LocalError協(xié)議

如果只是用Error還不足以表達(dá)更詳盡的錯(cuò)誤信息,可以使用LocalizedError協(xié)議,其定義如下

public protocol LocalizedError : Error {

    /// A localized message describing what error occurred.錯(cuò)誤描述
    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 }
}

  • 繼續(xù)修改上面的JSON解析尽纽,對(duì)JSONMapError枚舉增加一個(gè)擴(kuò)展咐蚯,遵守LocalizedError協(xié)議,可以打印更詳細(xì)的錯(cuò)誤信息
//定義更詳細(xì)的錯(cuò)誤信息
extension JSONMapError: LocalizedError{
    var errorDescription: String?{
        switch self {
        case .emptyKey:
            return "key為空"
        case .notConformProtocol:
            return "沒有遵守協(xié)議"
        }
    }
}

<!--使用-->
var t = CJLTeacher()
do{
    try t.jsonMap()
}catch{
    print(error.localizedDescription)
}

運(yùn)行結(jié)果如下

image

CustomNSError協(xié)議

CustomNSError相當(dāng)于OC中的NSError弄贿,其定義如下春锋,有三個(gè)默認(rèn)屬性

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 }
}

  • 繼續(xù)修改JSON解析中的JSONMapError,讓其遵守CustomNSError協(xié)議差凹,如下所示
//CustomNSError相當(dāng)于NSError
extension JSONMapError: CustomNSError{
    var errorCode: Int{
        switch self {
        case .emptyKey:
            return 404
        case .notConformProtocol:
            return 504
        }
    }
}

<!--使用-->
var t = CJLTeacher()
do{
    try t.jsonMap()
}catch{
    print((error as? JSONMapError)?.errorCode)
}

運(yùn)行結(jié)果如下

image

總結(jié)

  • Error是swift中錯(cuò)誤類型的基本協(xié)議期奔,其中LocalizedErrorCustomNSError都遵守Error

  • 如果在方法中危尿,想要同時(shí)返回正常值錯(cuò)誤呐萌,需要通過throw返回錯(cuò)誤,并且在方法返回值的箭頭前面加throws關(guān)鍵字谊娇,再調(diào)用方法時(shí)肺孤,還需要加上try關(guān)鍵字,或者使用do-catch济欢,使用try時(shí)赠堵,有以下兩點(diǎn)需要注意:

    • try? 返回的是一個(gè)可選類型,要么成功法褥,返回正常值茫叭,要么失敗,返回nil

    • try! 表示你對(duì)自己的代碼非常自信半等,絕對(duì)不會(huì)發(fā)生錯(cuò)誤杂靶,一旦發(fā)生錯(cuò)誤,就會(huì)崩潰

    • 使用建議:建議使用try?酱鸭,而不是try!

最后附上完整的自定義JSON解析代碼

//定義錯(cuò)誤類型
enum JSONMapError: Error{
    case emptyKey
    case notConformProtocol
}

//定義更詳細(xì)的錯(cuò)誤信息
extension JSONMapError: LocalizedError{
    var errorDescription: String?{
        switch self {
        case .emptyKey:
            return "key為空"
        case .notConformProtocol:
            return "沒有遵守協(xié)議"
        }
    }
}

//CustomNSError相當(dāng)于NSError
extension JSONMapError: CustomNSError{
    var errorCode: Int{
        switch self {
        case .emptyKey:
            return 404
        case .notConformProtocol:
            return 504
        }
    }
}

//1吗垮、定義一個(gè)JSON解析協(xié)議
protocol CustomJSONMap {
    func jsonMap() throws-> Any
}
//2、提供一個(gè)默認(rèn)實(shí)現(xiàn)
extension CustomJSONMap{
    func jsonMap() throws -> Any{
        let mirror = Mirror(reflecting: self)
        //遞歸終止條件
        guard !mirror.children.isEmpty else {
            return self
        }
        //字典凹髓,用于存儲(chǔ)json數(shù)據(jù)
        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{}

//3烁登、讓類遵守協(xié)議(注意:類中屬性的類型也需要遵守協(xié)議,否則無法解析)
class CJLTeacher: CustomJSONMap {
    var age = 18
    var name = "CJL"
    var height = 1.85
}

//使用
var t = CJLTeacher()

do{
    try t.jsonMap()
}catch{
    print((error as? JSONMapError)?.errorCode)
}

3蔚舀、 Mirror源碼解析

本文主要是分析Mirror的底層實(shí)現(xiàn)饵沧,以及根據(jù)Mirror底層原理仿寫其結(jié)構(gòu)的實(shí)現(xiàn)

Swift-進(jìn)階 06:反射Mirror & 錯(cuò)誤處理文章中,我們介紹了Mirror的使用赌躺,即JSON解析狼牺,對(duì)此我們有如下一些疑問:

  • 1、系統(tǒng)是如何通過Mirror獲取對(duì)應(yīng)的屬性以及值的礼患?

  • 2是钥、Swift眾所周知是一門靜態(tài)語言掠归,系統(tǒng)在底層到底做了什么,使swift具有了反射的特性呢悄泥?

下面我們來對(duì)Mirror的底層實(shí)現(xiàn)進(jìn)行探索

Mirror底層源碼分析

反射的API主要是由兩部分實(shí)現(xiàn)的

  • 一部分是通過Swift實(shí)現(xiàn)虏冻,即ReflectionMirror.swift

  • 一部分是通過C++實(shí)現(xiàn),即ReflectionMirror.mm

  • 兩者之間是通過暴露給swift的C++函數(shù)進(jìn)行通信弹囚,即@_silgen_name修飾符會(huì)通知swift編譯器將這個(gè)swift函數(shù)映射成C++函數(shù)的符號(hào)

  • Mirror的源碼是在Mirror.swift文件中,路徑為swift->stdlib->public->core->Mirror.swift

swift 使用技巧

使用@_silgen_name關(guān)鍵字聲明的方法厨相,實(shí)際調(diào)用是括號(hào)中的方法,例如swift_cjl_add實(shí)際調(diào)用的是c中的cjl_add

  • 通過C定義一個(gè)方法鸥鹉,在swift中使用
<!--1蛮穿、定義c方法-->
//.h聲明
int cjl_add(int a, int b);
//.c中實(shí)現(xiàn)
int cjl_add(int a, int b){
    return a+b;
}

<!--2、橋接文件中引入頭文件-->
#import "test.h"

<!--3毁渗、swift使用-->
var value = cjl_add(10, 20)
print(value)

<!--4绪撵、打印結(jié)果-->
30

  • 可以將上述代碼中的第2步去掉刪除,采用@_silgen_name關(guān)鍵字
<!--1祝蝠、swift中針對(duì)cjl_add方法的聲明-->
//通過@_silgen_name聲明
@_silgen_name("cjl_add")
func swift_cjl_add(_ a: Int32, _ b: Int32) -> Int32

<!--2、使用-->
var value = swift_cjl_add(20, 30)
print(value)

<!--3幻碱、打印結(jié)果-->
50

分析Mirror

  • Mirror.swift文件中找到Mirror绎狭,是一個(gè)結(jié)構(gòu)體類型

    image
  • 查找其初始化方法public init(reflecting subject: Any)

public init(reflecting subject: Any) {
    //判斷 subject 是否符合 CustomReflectable動(dòng)態(tài)類型
    if case let customized as CustomReflectable = subject {
      //如果符合,則由 customMirror 確定屬性
      self = customized.customMirror
    } else {
      //如果不符合褥傍,則由語言生成
      self = Mirror(internalReflecting: subject)
    }
}

  • 查找internalReflecting方法(路徑為swift->stdlib->public->core->ReflectionMirror.swift
extension Mirror {
  // Mirror的初始化器中檢索需要的信息
  /*
  - subject 將要被反射的值
  - subjectType 將要被反射的subject值的類型儡嘶,通常是值的運(yùn)行時(shí)類型
  - 
  */ 
  internal init(internalReflecting subject: Any,
              subjectType: Any.Type? = nil,
              customAncestor: Mirror? = nil)
  {
    //根據(jù)_getNormalizedType獲取傳入的subject的真正類型,其中type(of: subject)獲取的動(dòng)態(tài)類型
    let subjectType = subjectType ?? _getNormalizedType(subject, type: type(of: subject))
    // 獲取屬性大小
    let childCount = _getChildCount(subject, type: subjectType)
    // 遍歷恍风,將屬性存儲(chǔ)到字典中
    let children = (0 ..< childCount).lazy.map({
      // getChild函數(shù)時(shí)C++的_getChild 函數(shù)的簡(jiǎn)單封裝蹦狂,將標(biāo)簽名字中包含的C字符串轉(zhuǎn)換為Swift字符串
      getChild(of: subject, type: subjectType, index: $0)
    })
    // 賦值給Mirror的屬性children
    self.children = Children(children)
    // 設(shè)置父類反射
    self._makeSuperclassMirror = {//按需求構(gòu)建父類的Mirror的閉包
      // 獲取傳入對(duì)象的類
      guard let subjectClass = subjectType as? AnyClass,
            // 獲取父類
            let superclass = _getSuperclass(subjectClass) else {
        return nil//非類的類型、沒有父類的類的Mirror朋贬,會(huì)獲取到nil
      }

      // 調(diào)用者可以用一個(gè)可作為父類的Mirror直接返回Mirror實(shí)例來指定自定義的祖先的表現(xiàn)
      // Handle custom ancestors. If we've hit the custom ancestor's subject type,
      // or descendants are suppressed, return it. Otherwise continue reflecting.
      if let customAncestor = customAncestor {
        if superclass == customAncestor.subjectType {
          return customAncestor
        }
        if customAncestor._defaultDescendantRepresentation == .suppressed {
          return customAncestor
        }
      }
      // 給相同值返回一個(gè)將 superclass作為 subjectType的新的Mirror
      return Mirror(internalReflecting: subject,
                    subjectType: superclass,
                    customAncestor: customAncestor)
    }

    // 獲取并解析顯示的樣式凯楔,并設(shè)置Mirror的其他屬性
    let rawDisplayStyle = _getDisplayStyle(subject)
    switch UnicodeScalar(Int(rawDisplayStyle)) {
    case "c": self.displayStyle = .class
    case "e": self.displayStyle = .enum
    case "s": self.displayStyle = .struct
    case "t": self.displayStyle = .tuple
    case "\0": self.displayStyle = nil
    default: preconditionFailure("Unknown raw display style '\(rawDisplayStyle)'")
    }

    self.subjectType = subjectType
    self._defaultDescendantRepresentation = .generated
  }
  // 快速查找
  internal static func quickLookObject(_ subject: Any) -> _PlaygroundQuickLook? {
#if _runtime(_ObjC)
    let object = _getQuickLookObject(subject)
    return object.flatMap(_getClassPlaygroundQuickLook)
#else
    return nil
#endif
  }
}

??
<!--superclassMirror屬性的底層返回-->
public var superclassMirror: Mirror? {
    return _makeSuperclassMirror()
}
nternal let _makeSuperclassMirror: () -> Mirror?

1、通過_getNormalizedType獲取傳入的subject的真正類型
2锦募、通過_getChildCount方法獲取類中的屬性個(gè)數(shù)
3摆屯、遍歷屬性,通過getChild方法(C++的_getChild 函數(shù)的簡(jiǎn)單封裝)將標(biāo)簽名字中包含的C字符串轉(zhuǎn)換為Swift字符串糠亩,并將屬性存儲(chǔ)到字典中虐骑,賦值給Mirror的屬性children
4、Mirror有一個(gè)屬性superclassMirror赎线,會(huì)返回該類的父類廷没,其底層是返回一個(gè)_makeSuperclassMirror屬性,用于保存父類的Mirror閉包垂寥。首先會(huì)通過subjectType獲取父類颠黎,然后按照需求構(gòu)建父類的Mirror閉包另锋,如果是非類的類型、沒有父類的類的Mirror盏缤,會(huì)獲取到nil砰蠢。反之,則直接返回一個(gè)可作為父類的Mirror的實(shí)例對(duì)象唉铜。
5台舱、獲取并解析顯示的樣式,并設(shè)置Mirror的其他屬性

  • 進(jìn)入_getNormalizedType的實(shí)現(xiàn)潭流,根據(jù)定義竞惋,最終會(huì)調(diào)用C++中的swift_reflectionMirror_normalizedType -> Call方法
@_silgen_name("swift_reflectionMirror_normalizedType")
internal func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
??
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
const Metadata *swift_reflectionMirror_normalizedType(OpaqueValue *value,
                                                      const Metadata *type,
                                                      const Metadata *T) {
  return call(value, T, type, [](ReflectionMirrorImpl *impl) { return impl->type; });
}
??
/*
- passedValue 實(shí)際需要傳入的swift的值的指針
- T 該值的靜態(tài)類型
- passedType 被顯式傳入且會(huì)用在反射過程中的類型
- f 傳遞被查找到的會(huì)被調(diào)用的實(shí)現(xiàn)的對(duì)象引用
- 返回值:返回f參數(shù)調(diào)用時(shí)的返回值
*/ 
template<typename F>
auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedType,
          const F &f) -> decltype(f(nullptr))
{
  // 獲取type
  const Metadata *type;
  OpaqueValue *value;
  std::tie(type, value) = unwrapExistential(T, passedValue);
  // 判斷傳入type是否為空,如果不為空灰嫉,則直接賦值給type
  if (passedType != nullptr) {
    type = passedType;
  }
  // 使用 ReflectionMirrorImpl 子類的實(shí)例去結(jié)束調(diào)用f拆宛,然后會(huì)調(diào)用這個(gè)實(shí)例上的方法去真正的工作完成
  auto call = [&](ReflectionMirrorImpl *impl) {
    // 返回的type是傳入非type
    impl->type = type;
    impl->value = value;
    auto result = f(impl);
    return result;
  };

  .....

  switch (type->getKind()) {
    case MetadataKind::Tuple: {//元組
        ......
    }
    case MetadataKind::Struct: {//結(jié)構(gòu)體
       ......
    }
    case MetadataKind::Enum://枚舉
    case MetadataKind::Optional: {//可選
     ......
    }

    ......
}

??
<!--unwrapExistential實(shí)現(xiàn)-->
static std::tuple<const Metadata *, OpaqueValue *>
unwrapExistential(const Metadata *T, OpaqueValue *Value) {
  // If the value is an existential container, look through it to reflect the
  // contained value.如果該值是一個(gè)存在的容器,請(qǐng)查看它以反映包含的值讼撒。
  // TODO: Should look through existential metatypes too, but it doesn't
  // really matter yet since we don't have any special mirror behavior for
  // concrete metatypes yet.
  while (T->getKind() == MetadataKind::Existential) {
    auto *existential
      = static_cast<const ExistentialTypeMetadata *>(T);

    // Unwrap the existential container.打開存在容器
    T = existential->getDynamicType(Value);
    Value = existential->projectValue(Value);

    // Existential containers can end up nested in some cases due to generic
    // abstraction barriers.  Repeat in case we have a nested existential.
  }
  return std::make_tuple(T, Value);
}

??
<!--getDynamicType的實(shí)現(xiàn)-->
template<> const Metadata *
ExistentialTypeMetadata::getDynamicType(const OpaqueValue *container) const {
// 根據(jù) 獲取此存在類型使用的表示形式 判斷
  switch (getRepresentation()) {
  case ExistentialTypeRepresentation::Class: {
    auto classContainer =
      reinterpret_cast<const ClassExistentialContainer*>(container);
    void *obj = classContainer->Value;
    return swift_getObjectType(reinterpret_cast<HeapObject*>(obj));
  }
  case ExistentialTypeRepresentation::Opaque: {
    auto opaqueContainer =
      reinterpret_cast<const OpaqueExistentialContainer*>(container);
    return opaqueContainer->Type;
  }
  case ExistentialTypeRepresentation::Error: {
    const SwiftError *errorBox
      = *reinterpret_cast<const SwiftError * const *>(container);
    return errorBox->getType();
  }
  }

  swift_runtime_unreachable(
      "Unhandled ExistentialTypeRepresentation in switch.");
}

call中主要是一個(gè)大型switch聲明和一些額外的代碼去處理特殊的情況浑厚,主要是會(huì)ReflectionMirrorImpl子類實(shí)例去結(jié)束調(diào)用 f,然后會(huì)調(diào)用這個(gè)實(shí)例上的方法去讓真正的工作完成根盒。

1钳幅、首先根據(jù)unwrapExistential獲取type類型,其依賴于metaData元數(shù)據(jù)
2炎滞、判斷傳入的passedType是否為空敢艰,如果不為空,則直接賦值給type
3册赛、使用 ReflectionMirrorImpl子類的實(shí)例去結(jié)束調(diào)用f钠导,然后會(huì)調(diào)用這個(gè)實(shí)例上的方法去真正的工作完成

swift-source源碼調(diào)試_getNormalizedType方法

  • 運(yùn)行swift源碼,在Call函數(shù)的第一行加斷點(diǎn)
  • 在源碼終端依次輸入下面代碼
class CJLTeacher{var age = 18}
var t = CJLTeacher()
let mirror = Mirror(reflecting: t)

運(yùn)行結(jié)果如下森瘪,會(huì)在call的調(diào)用處斷住

image

其中參數(shù)的調(diào)試結(jié)果如下:

  • 其中valueMirror實(shí)例

  • typetype(of:)獲取的類型

  • T是自己傳入的類型

    image

ReflectionMirrorImpl 反射基類

ReflectionMirrorImpl的種類主要有以下幾種:

  • TupleImpl 元組的反射

  • StructImpl 結(jié)構(gòu)體的反射

  • EnumImpl 枚舉的反射

  • ClassImpl 類的反射

  • MetatypeImpl 元數(shù)據(jù)的反射

  • OpaqueImpl 不透明類型的反射

這里主要以Struct的反射為例

  • 首先查看ReflectionMirrorImpl的底層定義
// Abstract base class for reflection implementations.
struct ReflectionMirrorImpl {
  const Metadata *type;
  OpaqueValue *value;

  // 顯示的樣式
  virtual char displayStyle() = 0;
  // 屬性個(gè)數(shù)
  virtual intptr_t count() = 0;
  // 獲取偏移值
  virtual intptr_t childOffset(intptr_t index) = 0;
  // 獲取元數(shù)據(jù)
  virtual const FieldType childMetadata(intptr_t index,
                                        const char **outName,
                                        void (**outFreeFunc)(const char *)) = 0;
  //獲取屬性
  virtual AnyReturn subscript(intptr_t index, const char **outName,
                              void (**outFreeFunc)(const char *)) = 0;
  //獲取枚舉的case名字
  virtual const char *enumCaseName() { return nullptr; }

#if SWIFT_OBJC_INTEROP
// 快速查找
  virtual id quickLookObject() { return nil; }
#endif

  // For class types, traverse through superclasses when providing field
  // information. The base implementations call through to their local-only
  // counterparts.
  // 遞歸查找父類的屬性
  virtual intptr_t recursiveCount() {
    return count();
  }
  // 遞歸查找父類屬性的偏移值
  virtual intptr_t recursiveChildOffset(intptr_t index) {
    return childOffset(index);
  }
  // 遞歸獲取父類的元數(shù)據(jù)
  virtual const FieldType recursiveChildMetadata(intptr_t index,
                                                 const char **outName,
                                                 void (**outFreeFunc)(const char *))
  {
    return childMetadata(index, outName, outFreeFunc);
  }
// 析構(gòu)函數(shù)
  virtual ~ReflectionMirrorImpl() {}
};

  • 進(jìn)入StructImpl結(jié)構(gòu)體的底層實(shí)現(xiàn)牡属,需要注意一下幾點(diǎn):
// Implementation for structs.
// ReflectionMirrorImpl 的子類 StructImpl 結(jié)構(gòu)體反射
struct StructImpl : ReflectionMirrorImpl {
  bool isReflectable() {//是否支持反射
    const auto *Struct = static_cast<const StructMetadata *>(type);
    const auto &Description = Struct->getDescription();
    return Description->isReflectable();
  }
  // 用 s 的顯式樣式來表明這是一個(gè)結(jié)構(gòu)體
  char displayStyle() {
    return 's';
  }

  intptr_t count() {
    if (!isReflectable()) {
      return 0;
    }
// 首先也是找到metadata,然后通過metadata找到desc扼睬,然后找到fields湃望,即 NumFields 記錄屬性的count
    auto *Struct = static_cast<const StructMetadata *>(type);
    return Struct->getDescription()->NumFields;//屬性的count
  }

  intptr_t childOffset(intptr_t i) {
    auto *Struct = static_cast<const StructMetadata *>(type);
    // 邊界檢查
    if (i < 0 || (size_t)i > Struct->getDescription()->NumFields)
      swift::crash("Swift mirror subscript bounds check failure");

    // Load the offset from its respective vector.
    // 獲取偏移值
    return Struct->getFieldOffsets()[I];
  }

  const FieldType childMetadata(intptr_t i, const char **outName,
                                void (**outFreeFunc)(const char *)) {
    StringRef name;
    FieldType fieldInfo;
    //通過getFieldAt獲取屬性的名稱
    std::tie(name, fieldInfo) = getFieldAt(type, i);
    assert(!fieldInfo.isIndirect() && "indirect struct fields not implemented");

    *outName = name.data();
    *outFreeFunc = nullptr;

    return fieldInfo;
  }
// subscript 用來獲取當(dāng)前屬性的名稱和值
  AnyReturn subscript(intptr_t i, const char **outName,
                      void (**outFreeFunc)(const char *)) {
    // 獲取metaadata
    auto fieldInfo = childMetadata(i, outName, outFreeFunc);

    auto *bytes = reinterpret_cast<char*>(value);
    // 獲取屬性的偏移值
    auto fieldOffset = childOffset(i);
    // 計(jì)算字段存儲(chǔ)的指針
    auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);

    return copyFieldContents(fieldData, fieldInfo);
  }
};

1、count方法中屬性個(gè)數(shù)的獲取痰驱,是通過metadata证芭,然后找到其desc,然后找到NumFields獲取的担映,即NumFields 記錄屬性的count
2废士、subscript方法主要用來獲取當(dāng)前屬性的名稱和值

  • 首先獲取metadata
  • 然后獲取屬性的偏移值fieldOffset
  • 通過首地址+偏移值,計(jì)算屬性存儲(chǔ)的指針

其他幾種的分析類似,這里不再作說明

仿寫Mirror結(jié)構(gòu)

以上源碼說了這么多,是不是還是有點(diǎn)難以理解俊马,下面我們通過仿寫底層的結(jié)構(gòu)來幫助理解Mirror獲取屬性和值的原理

TargetStructMetadata結(jié)構(gòu)體:struct的反射類

  • 從Struct的反射類StructImpl中可以知道丽旅,其type的類型是StructMetadata

    image
  • Metadata.h文件中搜索StructMetadata,其真正的類型是TargetStructMetadata

using StructMetadata = TargetStructMetadata<InProcess>;

  • TargetStructMetadata -> TargetValueMetadata結(jié)構(gòu)體鹏溯,而
    TargetValueMetadata繼承自TargetMetadata它碎,在Swift-進(jìn)階 02:類券敌、對(duì)象岖研、屬性文章中卿操,我們已經(jīng)知道,TargetMetadata中有一個(gè)屬性kind(相當(dāng)于OC中的isa)孙援,而TargetValueMetadata除了擁有父類的kind害淤,還有一個(gè)description,用于記錄元數(shù)據(jù)的描述
/// The common structure of metadata for structs and enums.
template <typename Runtime>
struct TargetValueMetadata : public TargetMetadata<Runtime> {
  using StoredPointer = typename Runtime::StoredPointer;
  TargetValueMetadata(MetadataKind Kind,
                      const TargetTypeContextDescriptor<Runtime> *description)
      : TargetMetadata<Runtime>(Kind), Description(description) {}

    //用于記錄元數(shù)據(jù)的描述
  /// An out-of-line description of the type.
  TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;

    ......
}

TargetValueTypeDescriptor類:記錄metadata信息

  • 由上面可知拓售,Description的類型是TargetValueTypeDescriptor窥摄,其中有兩個(gè)屬性
    • NumFields 用于記錄屬性的count
    • FieldOffsetVectorOffset 用于記錄屬性在metadata中便宜向量的偏移量
template <typename Runtime>
class TargetStructDescriptor final
    : public TargetValueTypeDescriptor<Runtime>,
      public TrailingGenericContextObjects<TargetStructDescriptor<Runtime>,
                            TargetTypeGenericContextDescriptorHeader,
                            /*additional trailing objects*/
                            TargetForeignMetadataInitialization<Runtime>,
                            TargetSingletonMetadataInitialization<Runtime>> {
    ......

    /// The number of stored properties in the struct.
  /// If there is a field offset vector, this is its length.
  uint32_t NumFields;//記錄屬性的count
  /// The offset of the field offset vector for this struct's stored
  /// properties in its metadata, if any. 0 means there is no field offset
  /// vector.
  uint32_t FieldOffsetVectorOffset;//記錄屬性在metadata中便宜向量的偏移量

    ......
}

  • 進(jìn)入其繼承鏈TargetValueTypeDescriptor -> TargetTypeContextDescriptor類,其中有3個(gè)屬性
    • Name 用于記錄類型的名稱础淤,標(biāo)識(shí)當(dāng)前的類型
    • AccessFunctionPtr 指向此類型的metadata訪問函數(shù)的指針
    • Fields 指向類型的descriptor的指針
template <typename Runtime>
class TargetTypeContextDescriptor
    : public TargetContextDescriptor<Runtime> {
public:
  /// The name of the type. 類型的名稱
  TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name;

  /// A pointer to the metadata access function for this type.
  /// 指向此類型的元數(shù)據(jù)訪問函數(shù)的指針
  /// The function type here is a stand-in. You should use getAccessFunction()
  /// to wrap the function pointer in an accessor that uses the proper calling
  /// convention for a given number of arguments.
  TargetRelativeDirectPointer<Runtime, MetadataResponse(...),
                              /*Nullable*/ true> AccessFunctionPtr;

  /// A pointer to the field descriptor for the type, if any.指向類型的字段描述符的指針
  TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
                              /*nullable*/ true> Fields;

    ......
}

  • 進(jìn)入TargetContextDescriptor基類的定義崭放,其中有兩個(gè)參數(shù)
    • Flags 用于表示描述context的標(biāo)志,包含kind和version
    • Parent 用于表示父類的context鸽凶,如果是在頂層币砂,則表示沒有父類,則為NULL
/// Base class for all context descriptors.
template<typename Runtime>
struct TargetContextDescriptor {
  /// Flags describing the context, including its kind and format version.
  ContextDescriptorFlags Flags;

  /// The parent context, or null if this is a top-level context.
  TargetRelativeContextPointer<Runtime> Parent;

  ......
}

從上述的分析中我們可以得知吱瘩,

  • 屬性的獲取時(shí)通過baseDesc->Fields.get();(在ReflectionMirror.mm文件中getFieldAt方法),即是通過Description中的Fields屬性獲取迹缀,所以還需要分析Fields的類型TargetRelativeDirectPointer使碾,其內(nèi)部的類型是FieldDescriptor

RelativeDirectPointerImpl類:存放offset偏移量

  • TargetRelativeDirectPointer的真正類型是RelativeDirectPointer -> RelativeDirectPointerImpl,RelativeDirectPointerImpl主要用于存放offset偏移量
    • 屬性RelativeOffset,用于表示屬性的相對(duì)偏移值祝懂,而不是直接存儲(chǔ)地址票摇,如下所示

      image
    • 其中PointerTy、ValueTy就是傳入的類型T砚蓬、T的指針類型

template<typename T, bool Nullable, typename Offset>
class RelativeDirectPointerImpl {
private:
  /// The relative offset of the function's entry point from *this.
  Offset RelativeOffset;

    ......

public:
  using ValueTy = T;//是一個(gè)值
  using PointerTy = T*;//是一個(gè)指針
}
    //get方法 - 用于獲取屬性
  PointerTy get() const & {
    // Check for null.檢查是否為空
    if (Nullable && RelativeOffset == 0)
      return nullptr;

    // The value is addressed relative to `this`. 值是相對(duì)于“this”尋址的
    uintptr_t absolute = detail::applyRelativeOffset(this, RelativeOffset);
    return reinterpret_cast<PointerTy>(absolute);
  }
    ......
}

??
<!--applyRelativeOffset的實(shí)現(xiàn)-->
template<typename BasePtrTy, typename Offset>
static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) {
  static_assert(std::is_integral<Offset>::value &&
                std::is_signed<Offset>::value,
                "offset type should be signed integer");
// 指針地址
  auto base = reinterpret_cast<uintptr_t>(basePtr);
  // We want to do wrapping arithmetic, but with a sign-extended
  // offset. To do this in C, we need to do signed promotion to get
  // the sign extension, but we need to perform arithmetic on unsigned values,
  // since signed overflow is undefined behavior.
  auto extendOffset = (uintptr_t)(intptr_t)offset;
  return base + extendOffset;//指針地址+存放的offset(偏移地址) -- 內(nèi)存平移獲取值
}

FieldDescriptor類:存放屬性

進(jìn)入FieldDescriptor類的定義矢门,如下所示

class FieldDescriptor {
  const FieldRecord *getFieldRecordBuffer() const {
    return reinterpret_cast<const FieldRecord *>(this + 1);
  }

public:
  const RelativeDirectPointer<const char> MangledTypeName;
  const RelativeDirectPointer<const char> Superclass;

    ......

  const FieldDescriptorKind Kind;
  const uint16_t FieldRecordSize;
  const uint32_t NumFields;

    ......

  // 獲取所有屬性,每個(gè)屬性用FieldRecord封裝
   llvm::ArrayRef<FieldRecord> getFields() const {
    return {getFieldRecordBuffer(), NumFields};
  }

  ......
} 

FieldRecord類:封裝屬性

進(jìn)入FieldRecord類灰蛙,其定義如下

class FieldRecord {
  const FieldRecordFlags Flags;

public:
  const RelativeDirectPointer<const char> MangledTypeName;
  const RelativeDirectPointer<const char> FieldName;

上面主要分析了結(jié)構(gòu)體通過Mirror獲取屬性和值涉及的類和結(jié)構(gòu)體祟剔,其結(jié)構(gòu)仿寫代碼如下

/// metadata元數(shù)據(jù)
struct StructMetadata {
//    (取自類 - TargetMetadata:kind)
    //(繼承關(guān)系:TargetStructMetadata -> TargetValueMetadata -> TargetMetadata)
    var kind: Int
//    (取自結(jié)構(gòu)體 -  TargetValueMetadata:Description)
    var desc: UnsafeMutablePointer<StructMetadataDesc>
}

/// metada的描述信息
struct StructMetadataDesc {
//    (取自底層結(jié)構(gòu)體 - TargetContextDescriptor:flags + parent)
    var flags: Int32
    var parent: Int32
//    (取自底層類 - TargetTypeContextDescriptor:name + AccessFunctionPtr + Fields)
    //type的名稱
    //相對(duì)指針位置的存儲(chǔ)
    var name: RelativeDirectPointer<CChar>
    //補(bǔ)充完整
    var AccessFunctionPtr: RelativeDirectPointer<UnsafeRawPointer>
    //是通過Fields的getFiledName獲取屬性名稱
    var Fields: RelativeDirectPointer<FieldDescriptor>
//    (取自底層類 - TargetClassDescriptor:NumFields + FieldOffsetVectorOffset)
    //屬性的count
    var NumFields: Int32
    var FieldOffsetVectorOffset: Int32
}

/// 屬性的描述信息
//(取自底層類 - FieldDescriptor)
struct FieldDescriptor {
    var MangledTypeName: RelativeDirectPointer<CChar>
    var Superclass: RelativeDirectPointer<CChar>
    var Kind: UInt16
    var FieldRecordSize: Int16
    var NumFields: Int32
    //每個(gè)屬性都是FieldRecord,記錄在這個(gè)結(jié)構(gòu)體中
    var fields: FieldRecord//數(shù)組中是一個(gè)連續(xù)的存儲(chǔ)空間
}

/// 屬性封裝類
//(取自底層類 - FieldRecord)
struct FieldRecord{
    var Flags: Int32
    var MangledTypeName: RelativeDirectPointer<CChar>
    var FieldName: RelativeDirectPointer<CChar>
}

/// 記錄offset偏移值
struct RelativeDirectPointer<T>{
    var offset: Int32

    //模擬RelativeDirectPointerImpl類中的get方法 this+offset指針
    mutating func get() -> UnsafeMutablePointer<T>{
        let offset = self.offset
        return withUnsafePointer(to: &self) { p in
            /*
             獲得self摩梧,變?yōu)閞aw物延,然后+offset

             - UnsafeRawPointer(p) 表示this
             - advanced(by: numericCast(offset) 表示移動(dòng)的步長,即offset
             - assumingMemoryBound(to: T.self) 表示假定類型是T仅父,即自己制定的類型
             - UnsafeMutablePointer(mutating:) 表示返回的指針類型
            */
            return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
        }
    }
}

其使用如下

  • 定義一個(gè)CJLTeacher類
struct CJLTeacher {
    var age = 18
    var name = "CJL"
}

  • 1叛薯、首先獲取指向metadata的指針
  • 2浑吟、然后獲取字符串的地址,例如name耗溜,并讀取內(nèi)存值
/將t1綁定到StructMetadata(unsafeBitCast-按位強(qiáng)轉(zhuǎn)组力,非常危險(xiǎn),沒有任何校驗(yàn)抖拴、沒有任何修飾)
//unsafeBitCast - 所有的內(nèi)存按位轉(zhuǎn)換
//1燎字、先獲取指向metadata的指針
let ptr = unsafeBitCast(CJLTeacher.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)

//2、然后獲取字符串的地址
/*
 ptr.pointee 表示StructMetadata
 ptr.pointee.desc.pointee 表示StructMetadataDesc
 ptr.pointee.desc.pointee.name 表示RelativeDirectPointer<T>
 */
let namePtr = ptr.pointee.desc.pointee.name.get()
print(String(cString: namePtr))
//讀取內(nèi)存值
print(ptr.pointee.desc.pointee.NumFields)

  • 3城舞、獲取首地址轩触,通過指針移動(dòng)來獲取訪問屬性的地址,例如輸出age屬性
//獲取首地址
let filedDescriptorPtr = ptr.pointee.desc.pointee.Fields.get()
//指針移動(dòng)來獲取訪問屬性的地址
let recordPtr = withUnsafePointer(to: &filedDescriptorPtr.pointee.fields) {
    /*
     - UnsafeRawPointer + assumingMemoryBound -- 類型指針
     - advanced 類型指針只需要移動(dòng) 下標(biāo)即可
     */
    return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: FieldRecord.self).advanced(by: 0))
}
//輸出age屬性
print(String(cString: recordPtr.pointee.FieldName.get()))

如果將advanced中的0改成1家夺,輸出name

image

總結(jié)

所以綜上所述脱柱,Mirror反射干的事情:

  • 1、Mirror在實(shí)例對(duì)象的metadata中找到Descriptor

  • 2拉馋、在Descriptor

    • 找到name榨为,獲取類型(相當(dāng)于type名稱)
    • 找到numFields,獲取屬性個(gè)數(shù)
  • 3煌茴、找到FieldDescriptor中的fields随闺,來找到對(duì)當(dāng)前屬性的描述,然后通過指針移動(dòng)蔓腐,獲取其他屬性
    以上就是整個(gè)mirror在底層做的事情矩乐,如下所示,以struct為例的一個(gè)流程

    image

【補(bǔ)充】

  • swift中的type(of:)回论、dump(t)就是基于Mirror的反射原理來實(shí)現(xiàn)的

  • swift中JSON解析的三方庫HandyJSON其原理就是Mirror反射的原理散罕,本質(zhì)就是利用metadata元數(shù)據(jù)中的descriptor,然后通過字段的訪問傀蓉,做內(nèi)存的賦值(后續(xù)會(huì)完整分析HandyJSON)

所以欧漱,針對(duì)我們開頭的兩個(gè)問題,可以通過上面的Mirror反射的原理來進(jìn)行解釋
1葬燎、系統(tǒng)是如何通過Mirror獲取對(duì)應(yīng)的屬性以及值的误甚?
參考上述的Mirror原理總結(jié)

2、Swift眾所周知是一門靜態(tài)語言谱净,系統(tǒng)在底層到底做了什么窑邦,使swift具有了反射的特性呢?
Swift 的反射機(jī)制是基于一個(gè)叫 Mirror 的 struct 來實(shí)現(xiàn)的壕探。即為具體的 subject 創(chuàng)建一個(gè) Mirror奕翔,然后就可以通過它查詢這個(gè)對(duì)象subject。簡(jiǎn)單理解就是 Mirror通過meatadata浩蓉,在其內(nèi)部創(chuàng)建了一個(gè)結(jié)構(gòu)派继,用于輸出metadata中的descriptor

內(nèi)容參考:http://www.reibang.com/p/9af291059cdc

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宾袜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子驾窟,更是在濱河造成了極大的恐慌庆猫,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绅络,死亡現(xiàn)場(chǎng)離奇詭異月培,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)恩急,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門杉畜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人衷恭,你說我怎么就攤上這事此叠。” “怎么了随珠?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵灭袁,是天一觀的道長。 經(jīng)常有香客問我窗看,道長茸歧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任显沈,我火速辦了婚禮软瞎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拉讯。我一直安慰自己涤浇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布遂唧。 她就那樣靜靜地躺著芙代,像睡著了一般吊奢。 火紅的嫁衣襯著肌膚如雪盖彭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天页滚,我揣著相機(jī)與錄音召边,去河邊找鬼。 笑死裹驰,一個(gè)胖子當(dāng)著我的面吹牛隧熙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幻林,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼贞盯,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼音念!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起躏敢,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤闷愤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后件余,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讥脐,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年啼器,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旬渠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡端壳,死狀恐怖告丢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情更哄,我是刑警寧澤芋齿,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站成翩,受9級(jí)特大地震影響觅捆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜麻敌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一栅炒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧术羔,春花似錦赢赊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至寥殖,卻和暖如春玩讳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嚼贡。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工熏纯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粤策。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓樟澜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秩贰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容