Swift底層探索(七)Swift的反射Mirror的實現和錯誤處理

語言: CN / TW / HK

我正在參加「掘金·啟航計劃」

本文主要對Swift的反射實現有一個基本的認識,通過JSON解析來認識反射以及錯誤的實際使用。

主要內容 1. Mirror的使用 2. 錯誤Error的使用

1、 Mirror

Swift是一門靜態語言,沒有像OC中Runtime那樣的執行時操作。但是Swift標準庫中提供了反射機制,用來動態訪問成員資訊,這就是Mirror。(當然遠沒有Runtime那樣強大)

1.1 基本使用

程式碼: ``` / 1、使用 / class WYTeacher: NSObject { var age: Int = 18 } var t = WYTeacher() let mirror = Mirror(reflecting: t.self) for pro in mirror.children{ print("(String(describing: pro.label)): (pro.value)") }

//Optional("age"): 18 ```

說明: * 傳入的是一個物件 * 獲取到MIrror物件,通過它就可以獲取到t物件的成員,分別通過label、value獲取屬性和值

1.2 Mirror認識

Mirror的初始化: public init(reflecting subject: Any) 說明: * 進入Mirror初始化方法,發現傳入的型別是Any, * 正是這個原因,我們才可以直接傳t * 建立在給定例項上進行反射的反射物件 * 如果subject的型別遵守CustomReflectable協議,生成的mirror由customMirror屬性決定

children public let children: Mirror.Children 👇 //進入Children,發現是一個AnyCollection,接收一個泛型 public typealias Children = AnyCollection<Mirror.Child> 👇 //進入Child,發現是一個元組型別,由可選的標籤和值構成, public typealias Child = (label: String?, value: Any) 說明: * children的型別為Children * 而Children是AnyCollection的別名,是所有Child的集合 * Child又是一個元組,由label、value組成 * 分別用來儲存屬性和值

2、JOSN的實現

通過反射可以很輕鬆的獲取到一個物件的成員,因此我們可以用來進行JSON解析,將一個物件轉換成JSON格式

程式碼: ``` / 2、JSON解析 / //Int、String遵守協議 extension Int: CustomJSONMap{} extension String: CustomJSONMap{}

//1、定義一個JSON解析協議 protocol CustomJSONMap { func jsonMap() -> Any } //2、提供一個預設實現 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 } }

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

//使用 var t = WYTeacher() print(t.jsonMap()) ``` 說明: * 專門定義一個用來進行JSON解析的協議,這樣可以給你任意型別的物件進行解析,只需要這個型別遵守該協議即可 * 這裡的實現也就是拿到物件的成員,分別儲存到字典中,之後返回 * 這裡進行了遞迴呼叫,因為有可能屬性本身也是一個物件。以一個字典的形式再儲存到這個key的value上 * 類中屬性的型別也需要遵守協議,因為這裡涉及到遞迴呼叫

3、Error的使用

在上文對於錯誤的處理是僅僅進行列印,這樣並不規範,Swift中也提供了Error的處理方式。

Error的認識可以看我的另一篇部落格Swift語法(十)Error

3.1 Error

Swift中,提供了Error協議來標識當前應用程式發生錯誤的情況。struct、Class、enum都可以遵守該協議,自定義錯誤。

程式碼: ``` //定義錯誤型別 enum JSONMapError: Error{ case emptyKey case notConformProtocol }

protocol CustomJSONMap { func jsonMap() throws -> Any }

//2、提供一個預設實現 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 } }

var t = WYTeacher() print(try t.jsonMap()) ``` 說明: * 定義一個Error的列舉 * 可以使用throw丟擲異常 * 方法中通過throws將錯誤丟擲 * 可能丟擲異常的函式需要使用try呼叫,避免編譯報錯 * 但是僅僅使用try呼叫,沒有進行處理,這樣會閃退 * 可以使用try ... catch進行錯誤處理。具體的處理可以看另一篇部落格,這裡不再贅述

3.2 LocalError協議

如果只是用Error還不足以表達更詳盡的錯誤資訊,可以使用LocalizedError協議,其定義如下

定義: ``` 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 }

} ``` 說明: * LocalError協議提供了多種屬性 * 我們可以給協議的這些屬性增加描述,這樣在錯誤處理時就可以進行列印

使用: 此時不給Int和String遵守CustomJSONMap,因此會走.notConformProtocol的分支 ``` //定義更詳細的錯誤資訊 extension JSONMapError: LocalizedError{ var errorDescription: String?{ switch self { case .emptyKey: return "key為空" case .notConformProtocol: return "沒有遵守協議" } } }

var t = WYTeacher() do{ try t.jsonMap() }catch{ print(error.localizedDescription) } ```

執行結果:

16622120021553.jpg

3.3 CustomNSError協議

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 }

} ```

使用: ``` //CustomNSError相當於NSError extension JSONMapError: CustomNSError{ var errorCode: Int{ switch self { case .emptyKey: return 404 case .notConformProtocol: return 504 } } }

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

執行結果:

16622120841871.jpg

4、總結

  • Swift使用Mirror實現反射功能,可以動態獲取物件的屬性
  • 通過Mirror可以實現JSON的解析
  • Error是swift中錯誤型別的基本協議
  • 其中LocalizedError、CustomNSError都遵守Error,他們可以提供更詳細的資訊
  • try呼叫可能發生錯誤資訊的函式