Swift 中的反射

所謂反射就是可以動態(tài)獲取類型、成員信息,在運(yùn)行時可以調(diào)用方法、屬性等行為的特性。 在使用OC開發(fā)時很少強(qiáng)調(diào)其反射概念,因為OC的Runtime要比其他語言中的反射強(qiáng)大的多。不過在Swift中并不提倡使用Runtime,而是像其他語言一樣使用反射(Reflect),即使目前Swift中的反射功能還比較弱,只能訪問獲取類型、成員信息。

Swift的反射機(jī)制是基于一個叫Mirror的結(jié)構(gòu)體來實現(xiàn)的。你為具體的實例創(chuàng)建一個Mirror對象,然后就可以通過它查詢這個實例

Mirror結(jié)構(gòu)體常用屬性:
subjectType:對象類型
children:反射對象的屬性集合
displayStyle:反射對象展示類型
下面來簡單介紹下Mirror的使用:

class Person {
    var name:String?
    var age:Int = 0
}

let p = Person()
p.name = "Bobby"
p.age = 18

let mirror:Mirror = Mirror(reflecting: p)
print("獲取對象類型:\(mirror.subjectType)")
// 打印出:獲取對象類型Person

for p in mirror.children {
    let propertyNameString = p.label!  // 屬性名使用!,因為label是optional類型 
    let value = p.value                // 屬性的值
    print("\(propertyNameString)的值為:\(value)")
}

/* 打印:
name的值為Optional("Bobby")
age的值為18
*/

// 獲取指定索引下的屬性類型
let children = mirror.children
let p0 = children.startIndex.advancedBy(0)             // 獲取name屬性的位置索引
let p0Mirror =  Mirror(reflecting: children[p0].value) // name的反射
print("獲取屬性name的類型為\(p0Mirror.subjectType)")
// 打印:獲取屬性name的類型為Optional<String>

// 遍歷獲取對象所有動態(tài)的屬性類型
for p in mirror.children {
    let propertyNameString = p.label!
    let value = p.value
    let vMirror = Mirror(reflecting: value)  // 通過值來創(chuàng)建屬性的反射
    print("屬性\(propertyNameString)類型為:\(vMirror.subjectType)")
}
/* 打印:
屬性name類型為Optional<String>
屬性age類型為Int
*/

反射的應(yīng)用場景現(xiàn)在還比較狹窄,因為功能還不夠完善,我提供一個比較常見的反射應(yīng)用場景,那就是自定義類模型轉(zhuǎn)字典
以下就是自定義類模型轉(zhuǎn)字典實例

//: Playground - noun: a place where people can play

import Foundation
import UIKit

class User {
    var name:String = ""
    var nickname:String?
    var age:Int?
    var emails:[String]?
    var tels:[Telephone]?
}

// 電話結(jié)構(gòu)體
struct Telephone {
    var title:String   // 電話標(biāo)題
    var number:String  // 電話號碼
}

// 自定義一個JSON協(xié)議
protocol JSON {
    func toJSONModel() -> Any?
}

// 擴(kuò)展協(xié)議方法,實現(xiàn)一個通用的toJSONModel方法(反射實現(xiàn))
extension JSON {
    // 將模型數(shù)據(jù)轉(zhuǎn)成可用的字典數(shù)據(jù),Any表示任何類型,除了方法類型
    func toJSONModel() -> Any? {
        // 根據(jù)實例創(chuàng)建反射結(jié)構(gòu)體Mirror
        let mirror = Mirror(reflecting: self)
        
        if mirror.children.count > 0  {
            // 創(chuàng)建一個空字典,用于后面添加鍵值對
            var result: [String:Any] = [:]
            for children in mirror.children {
                let propertyNameString = children.label!
                let value = children.value
                // 判斷value的類型是否遵循JSON協(xié)議,進(jìn)行深度遞歸調(diào)用
                if let jsonValue = value as? JSON {
                    result[propertyNameString] = jsonValue.toJSONModel()
                }
            }
            return result
        }
        return self
    }
}

// 擴(kuò)展可選類型,使其遵循JSON協(xié)議,可選類型值為nil時,不轉(zhuǎn)化進(jìn)字典中
extension Optional: JSON {
    func toJSONModel() -> Any? {
        if let x = self {
            if let value = x as? JSON {
                return value.toJSONModel()
            }
        }
        return nil
    }
}

// 擴(kuò)展兩個自定義類型,使其遵循JSON協(xié)議
extension User: JSON { }
extension Telephone: JSON { }

// 擴(kuò)展Swift的基本數(shù)據(jù)類型,使其遵循JSON協(xié)議
extension String: JSON { }
extension Int: JSON { }
extension Bool: JSON { }
extension Dictionary: JSON { }
extension Array: JSON { }

// 創(chuàng)建一個User實例對象模型
let user1 = User()
user1.name = "hangge"
user1.age = 100
user1.emails = ["hangge@hangge.com","system@hangge.com"]
let tel1 = Telephone(title: "手機(jī)", number: "123456")
let tel2 = Telephone(title: "公司座機(jī)", number: "001-0358")
user1.tels = [tel1, tel2]

// 模型轉(zhuǎn)字典
if let model = user1.toJSONModel() {
    print(model)
}

/* 打?。骸疽韵麓蛴〗?jīng)過排版,正式的打印是緊湊的】
[
"tels": [
"[1]": [
"title": "公司座機(jī)",
"number": "001-0358"
],
"[0]": [
"title": "手機(jī)",
"number": "123456"
]
],
"name": "hangge",
"emails": [
"[1]": "system@hangge.com",
"[0]": "hangge@hangge.com"
],
"age": 100
]
*/
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,041評論 4 61
  • 大家好,在這里給大家推薦一部漫畫,既幽默又暖心,書名為《你今天真好看》,下面是我的臨摹~(別嫌棄哈) 求贊!
    子衿頤閱讀 324評論 1 2
  • 【30天月度檢視】郭志斌 第一組郭思彤家長 郭志斌 作者 2017.09.01 22: 00 【基本情況】 姓名:...
    配誌閱讀 461評論 0 0
  • 文/丁二剛 晚上霞董開著車載著我和花花、蛟一起去吃了路邊的大排檔,喝了泰國的白米粥,雖然路邊攤的東西往往會被別人貼...
    向光的小蟲閱讀 238評論 0 0
  • event事件問題 IE中有window.event,而火狐中沒有 獲取元素的非行間樣式值 獲取自定義屬性問題 I...
    金字笙調(diào)閱讀 678評論 0 3

友情鏈接更多精彩內(nèi)容