swift Mirror
Mirror
Mirror 是指可以動(dòng)態(tài)獲取類(lèi)型、成員信息,在運(yùn)行時(shí)可以調(diào)用方法、屬性等行為的特性
- 對(duì)于一個(gè)純swift類(lèi)來(lái)說(shuō),并不支持直接像OC runtime那樣的操作
- 但是swift標(biāo)準(zhǔn)庫(kù)依舊提供了反射機(jī)制,用來(lái)訪問(wèn)成員信息,即Mirror
創(chuàng)建一個(gè)類(lèi) Rocteacher
class Rocteacher {
var age = 18
}
創(chuàng)建解析方法
fileprivate func mirrorTesting()->Void{
// 應(yīng)該傳入實(shí)例對(duì)象
let t = Rocteacher()
let mirror = Mirror(reflecting: t.self)
for pro in mirror.children {
print("\(pro.label ?? "hello") : \(pro.value)")
}
}
// 使用
mirrorTesting()
// 結(jié)果
age : 18
- 查看Mirror定義
進(jìn)入Mirror初始化方法,發(fā)現(xiàn)傳入的類(lèi)型是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è)元組類(lèi)型,由可選的標(biāo)簽和值構(gòu)成,
public typealias Child = (label: String?, value: Any)
這也是為什么能夠通過(guò)label、value打印的原因。即可以在編譯時(shí)期且不用知道任何類(lèi)型信息情況下,在Child的值上用Mirror去遍歷整個(gè)對(duì)象的層級(jí)視圖
JSON解析
根據(jù)Mirror的這個(gè)特性,我們思考下,可以通過(guò)Mirror做什么?首先想到的是JSON解析,如下所示,我們定義了一個(gè)Rocteacher類(lèi),然后通過(guò)一個(gè)test方法來(lái)解析t
/**
JSON解析
*/
fileprivate func analyzingJson<T>(_ obj : T) -> Any{
let mirror = Mirror(reflecting: obj)
guard !mirror.children.isEmpty else {return obj}
// 創(chuàng)建字典
var keyValue = [String:Any]()
for children in mirror.children {
if let keyName = children.label {
keyValue[keyName] = (analyzingJson(children.value) as Any)
}
else {
print("children.label 為空")
}
}
return keyValue
}
// 使用
let t = Rocteacher()
let obj = analyzingJson(t.self)
print(obj)
// 結(jié)果
["age": 18]
JSON解析封裝
創(chuàng)建一個(gè)protocol
protocol CustomJson {
func jsonMap() -> Any
}
extension CustomJson 實(shí)現(xiàn)協(xié)議方法(注意 不能用fileprivate,否則外部文件中不能調(diào)用)
extension CustomJson {
func jsonMap() -> Any {
let mirror = Mirror(reflecting: self)
// 遞歸終止條件
guard !mirror.children.isEmpty else {return self}
// 創(chuàng)建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
print("keyName == nil")
}
}
else{
print("當(dāng)前-\(children.value)-沒(méi)有遵守協(xié)議")
}
}
return mapObject
}
}
創(chuàng)建一個(gè)遵循 CustomJson(protocol) 的類(lèi)
// 創(chuàng)建
class RocmapJsonObject:CustomJson {
let age = 18
let name = "roc"
}
使用 RocmapJsonObject
// JSON封裝類(lèi)使用
let mapJsonObject = RocmapJsonObject()
let mapJsonResult = mapJsonObject.jsonMap()
print(mapJsonResult)
// 結(jié)果
當(dāng)前-18-沒(méi)有遵守協(xié)議
當(dāng)前-roc-沒(méi)有遵守協(xié)議
解決 遵守協(xié)議的問(wèn)題
extension Int:CustomJson{}
extension String:CustomJson{}
再次使用 RocmapJsonObject
// JSON封裝類(lèi)使用
let mapJsonObject = RocmapJsonObject()
let mapJsonResult = mapJsonObject.jsonMap()
print(mapJsonResult)
// 結(jié)果
["age": 18, "name": "roc"]
Error協(xié)議
Swift中,提供了Error協(xié)議來(lái)標(biāo)識(shí)當(dāng)前應(yīng)用程序發(fā)生錯(cuò)誤的情況
Error協(xié)議
public protocol Error { // 這家伙本身就是一個(gè)協(xié)議}
Error是一個(gè)空協(xié)議,其中沒(méi)有任何實(shí)現(xiàn),這也就意味著你可以遵守該協(xié)議,然后自定義錯(cuò)誤類(lèi)型。所以不管是我們的struct、Class、enum,我們都可以遵循這個(gè)Error來(lái)表示一個(gè)錯(cuò)誤
所以接下來(lái),對(duì)我們上面封裝的JSON解析修改其中的錯(cuò)誤處理
使用 enum 定一個(gè)錯(cuò)誤類(lèi)型 遵循Error
enum JsonMapError:Error {
case Emptykey
case NotConformProtocol
}
首先--->注釋掉 Int,String 遵循CustomJson的定義
//extension Int:CustomJson{}
//extension String:CustomJson{}
方式一
1.修改 jsonMap (return)
extension CustomJson {
func jsonMap() -> Any {
let mirror = Mirror(reflecting: self)
// 遞歸終止條件
guard !mirror.children.isEmpty else {return self}
// 創(chuàng)建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
return JsonMapError.Emptykey
}
}
else{
// print("當(dāng)前-\(children.value)-沒(méi)有遵守協(xié)議")
return JsonMapError.NotConformProtocol
}
}
return mapObject
}
}
2.查看使用結(jié)果
// JSON封裝類(lèi)使用
let mapJsonObject = RocmapJsonObject()
let mapJsonResult = mapJsonObject.jsonMap()
print(mapJsonResult)
// 結(jié)果
NotConformProtocol
方式二
1.修改 jsonMap (throws)
extension CustomJson {
func jsonMap2()throws -> Any {
let mirror = Mirror(reflecting: self)
// 遞歸終止條件
guard !mirror.children.isEmpty else {return self}
// 創(chuàng)建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
throw JsonMapError.Emptykey
}
}
else{
// print("當(dāng)前-\(children.value)-沒(méi)有遵守協(xié)議")
throw JsonMapError.NotConformProtocol
}
}
return mapObject
}
}
2.查看使用結(jié)果
let mapJsonObject = RocmapJsonObject()
let mapJsonResult2 = try! mapJsonObject.jsonMap2()
print(mapJsonResult2 as Any)
// 結(jié)果
ViewController.swift:29: Fatal error: 'try!' expression unexpectedly raised an error: Swift_Case_1.(unknown context at $10ca3e338).JsonMapError.NotConformProtocol
try
使用try關(guān)鍵字,是最簡(jiǎn)便的,即甩鍋,將這個(gè)拋出給別人(向上拋出,拋給上層函數(shù))。但是在使用時(shí),需要注意以下兩點(diǎn):
-
try? 返回一個(gè)可選類(lèi)型,只有兩種結(jié)果:
- 要么成功,返回具體的字典值
- 要么錯(cuò)誤,但并不關(guān)心是哪種錯(cuò)誤,統(tǒng)一返回nil
try! 表示你對(duì)這段代碼有絕對(duì)的自信,這行代碼絕對(duì)不會(huì)發(fā)生錯(cuò)誤 或 絕對(duì)會(huì)發(fā)生錯(cuò)誤
do ... catch
通過(guò)do-catch來(lái)處理JSON解析的錯(cuò)誤
1.jsonMap2
extension CustomJson {
func jsonMap2()throws -> Any {
let mirror = Mirror(reflecting: self)
// 遞歸終止條件
guard !mirror.children.isEmpty else {return self}
// 創(chuàng)建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
throw JsonMapError.Emptykey
}
}
else{
// print("當(dāng)前-\(children.value)-沒(méi)有遵守協(xié)議")
throw JsonMapError.NotConformProtocol
}
}
return mapObject
}
}
2.查看使用結(jié)果
do {
let mapJsonResult3 = try mapJsonObject.jsonMap2()
print(mapJsonResult3)
}
catch {
print(error)
}
// 結(jié)果
NotConformProtocol
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 }
}
實(shí)現(xiàn)
1.jsonMap2
extension CustomJson {
func jsonMap2()throws -> Any {
let mirror = Mirror(reflecting: self)
// 遞歸終止條件
guard !mirror.children.isEmpty else {return self}
// 創(chuàng)建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
throw JsonMapError.Emptykey
}
}
else{
// print("當(dāng)前-\(children.value)-沒(méi)有遵守協(xié)議")
throw JsonMapError.NotConformProtocol
}
}
return mapObject
}
}
2.extension JsonMapError:LocalizedError
extension JsonMapError:LocalizedError {
// 重寫(xiě) errorDescription
var errorDescription: String? {
switch self{
case .Emptykey: return "key == nil"
case .NotConformProtocol: return "沒(méi)有遵循協(xié)議"
}
}
}
3.查看使用結(jié)果
// 方式四
do {
let mapJsonResult3 = try mapJsonObject.jsonMap2()
print(mapJsonResult3)
}
catch {
print(error.localizedDescription)
}
// 結(jié)果
沒(méi)有遵循協(xié)議
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 }
}
實(shí)現(xiàn)
1.創(chuàng)建一個(gè)enum 遵循Error 和 CustomNSError
enum JsonMapErrors:Error,CustomNSError{
case Emptykey
case NotConformProtocol
var errorCode: Int {
switch self {
case .Emptykey: return 404
case .NotConformProtocol: return 520
}
}
}
2.jsonMap3
extension CustomJson {
func jsonMap3()throws -> Any {
let mirror = Mirror(reflecting: self)
// 遞歸終止條件
guard !mirror.children.isEmpty else {return self}
// 創(chuàng)建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
throw JsonMapErrors.Emptykey
}
}
else{
// print("當(dāng)前-\(children.value)-沒(méi)有遵守協(xié)議")
throw JsonMapErrors.NotConformProtocol
}
}
return mapObject
}
}
3.查看使用結(jié)果
// 方式五
do {
let mapJsonResult3 = try mapJsonObject.jsonMap3()
print(mapJsonResult3)
}
catch {
print((error as! JsonMapErrors).errorCode)
}
// 結(jié)果
520
總結(jié)
Error是swift中錯(cuò)誤類(lèi)型的基本協(xié)議,其中LocalizedError、CustomNSError都遵守Error
- 如果在方法中,想要同時(shí)返回正常值和錯(cuò)誤,需要通過(guò)throw返回錯(cuò)誤,并且在方法返回值的箭頭前面加throws關(guān)鍵字,再調(diào)用方法時(shí),還需要加上try關(guān)鍵字,或者 使用do-catch
使用try時(shí),有以下兩點(diǎn)需要注意:
- try? 返回的是一個(gè)可選類(lèi)型,要么成功,返回正常值,要么失敗,返回nil
- try! 表示你對(duì)自己的代碼非常自信,絕對(duì)不會(huì)發(fā)生錯(cuò)誤,一旦發(fā)生錯(cuò)誤,就會(huì)崩潰
使用建議:建議使用try?,而不是try!
最后附上完整的自定義JSON解析代碼
- 創(chuàng)建協(xié)議并添加方法
protocol CustomJson{
func jsonMap3()throws -> Any
}
- 創(chuàng)建JsonMapErrors
// 創(chuàng)建JsonMapErrors
//extension Int:CustomJson{}
//extension String:CustomJson{}
enum JsonMapErrors:Error,LocalizedError,CustomNSError{
case Emptykey
case NotConformProtocol
var errorDescription: String? {
switch self {
case .Emptykey: return "keyName == nil"
case .NotConformProtocol: return "value 沒(méi)有遵守協(xié)議"
}
}
var errorCode: Int {
switch self {
case .Emptykey: return 404
case .NotConformProtocol: return 520
}
}
}
- 實(shí)現(xiàn)jsonMap3協(xié)議方法
extension CustomJson {
func jsonMap3()throws -> Any {
let mirror = Mirror(reflecting: self)
// 遞歸終止條件
guard !mirror.children.isEmpty else {return self}
// 創(chuàng)建字典
var mapObject:[String : Any] = [:]
for children in mirror.children {
if let value = children.value as? CustomJson{
if let keyName = children.label {
mapObject[keyName] = value.jsonMap()
}
else {
// print("keyName == nil")
throw JsonMapErrors.Emptykey
}
}
else{
// print("當(dāng)前-\(children.value)-沒(méi)有遵守協(xié)議")
throw JsonMapErrors.NotConformProtocol
}
}
return mapObject
}
}
- 創(chuàng)建 RocmapJsonObject
// 創(chuàng)建 RocmapJsonObject
class RocmapJsonObject:CustomJson {
let age = 18
let name = "roc"
}
- 使用 RocmapJsonObject
// JSON封裝類(lèi)使用
let mapJsonObject = RocmapJsonObject()
do {
let mapJsonResult3 = try mapJsonObject.jsonMap3()
print(mapJsonResult3)
}
catch {
print((error as! JsonMapErrors).errorCode)
}