Swift-Reflection

前言

熟悉 Java 的讀者可能會(huì)知道反射 (Reflection)鲁沥。這是一種在運(yùn)行時(shí)檢測(cè)方仿、訪問或者修改類型的行為的特性。一般的靜態(tài)語(yǔ)言類型的結(jié)構(gòu)和方法的調(diào)用等都需要在編譯時(shí)決定防症,開發(fā)者能做的很多時(shí)候只是使用控制流 (比如 if 或者 switch) 來決定做出怎樣的設(shè)置或是調(diào)用哪個(gè)方法。而反射特性可以讓我們有機(jī)會(huì)在運(yùn)行的時(shí)候通過某些條件實(shí)時(shí)地決定調(diào)用的方法山叮,或者甚至向某個(gè)類型動(dòng)態(tài)地設(shè)置甚至加入屬性及方法,是一種非常靈活和強(qiáng)大的語(yǔ)言特性添履。

Objective-C 中我們不太會(huì)經(jīng)常提及到 “反射” 這樣的詞語(yǔ)屁倔,因?yàn)?Objective-C 的運(yùn)行時(shí)比一般的反射還要靈活和強(qiáng)大∧弘剩可能很多讀者已經(jīng)習(xí)以為常的像是通過字符串生成類或者 selector锐借,并且進(jìn)而生成對(duì)象或者調(diào)用方法等,其實(shí)都是反射的具體的表現(xiàn)往衷。而在 Swift 中其實(shí)就算拋開 Objective-C 的運(yùn)行時(shí)的部分钞翔,在純 Swift 范疇內(nèi)也存在有反射相關(guān)的一些內(nèi)容,只不過相對(duì)來說功能要弱得多席舍。

摘抄自《Swifter: 100個(gè)Swift開發(fā)必備 Tip》

需求

  • 獲取類的屬性列表
  • 給屬性賦值

Swift 反射的實(shí)現(xiàn)方式

  • Mirror
  • OC runtime

方式一:Mirror

實(shí)現(xiàn)方式

獲取屬性的類型

func getTypeOfProperty (_ name: String) -> Any.Type {
    // 注意:self是實(shí)例(對(duì)象)布轿,如果是類,則無法獲取其屬性
    var type: Mirror = Mirror(reflecting: self)
    for child in type.children {
        if child.label! == name {
           return  type(of: child.value)
        }
    }
    while let parent = type.superclassMirror {
        for child in parent.children {
            if child.label! == name {
                return type(of: child.value)
            }
        }
        type = parent
    }
    return NSNull.Type.self
}

給屬性賦值

func setObjectParams(obj: NSObject, paramsDic:[String:Any]?) {
    if let paramsDic = paramsDic {
        for (key,value) in paramsDic {
            let type = obj.getTypeOfProperty(key)
            if type == NSNull.Type.self {
                print("[\(obj)]沒有[\(key)]參數(shù)")
            }else if …… {
                // ……
            }else {
                obj.setValue(value, forKey: key)
            }
        }
    }
}

特點(diǎn)

  • 簡(jiǎn)單(Swift 中所有的類型都實(shí)現(xiàn)了 _Reflectable)
  • 無法獲取類型的屬性来颤,只能獲取實(shí)例的屬性
  • 無法獲取寫的有getset方法的屬性

方式二:OC runtime

實(shí)現(xiàn)方式(部分代碼)

獲取屬性的類型

/// 獲取對(duì)象屬性類型列表
/// - 注意:這種方式獲取屬性汰扭,對(duì)于非引用類型的屬性,必須有初始值脚曾,否則無法獲取到东且!
/// - 參考自:https://github.com/Sajjon/SwiftReflection
///
/// - parameter clazz:         對(duì)象類型
///
/// - returns: 屬性名 & 類型 字典數(shù)組.
open class func propertyList(clazz: NSObject.Type) -> [[String: Any]]? {
    var count: UInt32 = 0
    let list = class_copyPropertyList(clazz, &count)
    var resultList = [[String: Any]]()
    for i in 0..<Int(count) {
        guard let pty = list?[i],
            let cName = getNameOf(property: pty),
            let name = String(utf8String: cName)
            else {
                continue
        }
        let type = getTypeOf(property: pty)
        resultList.append([name: type])
    }
    free(list)
    return resultList
}

給屬性賦值

/// 給對(duì)象賦值
///
/// - parameter paramsDict:         參數(shù)字典
/// - parameter obj:                待賦值的對(duì)象
/// - parameter complete:           賦值完成的回調(diào)
open class func setParams(_ paramsDict:[String:Any]?, for obj: NSObject, complete: (()->()) = {}) {
    if let paramsDict = paramsDict {
        let clazz: NSObject.Type = type(of: obj)
        let list = propertyList(clazz: clazz) ?? []
        var filteredList = [[String: Any]]()
        let _ = paramsDict.map({ dict in
            let tmp = list.filter({ $0.keys.contains(dict.key)}).first ?? [:]
            filteredList.append(tmp)
        })
        print("================= 賦值開始 =================")
        for (key, value) in paramsDict {
            // 取出key對(duì)應(yīng)的類型
            let value = "\(value)"
            let type = getType(key: key, typeDictList: filteredList)
            if InnerConst.BOOL == type {
                let toValue = value.toBool() ?? false
                obj.setValue(toValue, forKey: key)
            } else if …… {
                // ……
            } else if InnerConst.NULL == type {
                print("[\(obj)]沒有[\(key)]參數(shù)")
            }
        }
        print("================= 賦值完成 =================")
        complete()
    }
}

特點(diǎn)

  • 功能強(qiáng)大启具,可作用于任何類或者實(shí)例
  • 比較麻煩本讥,需要有 Objective-C 的基礎(chǔ)
  • 無法獲取沒有初始化的非引用類型屬性(值類型必須得初始化!)
  • 所作用的對(duì)象,必須繼承自NSObject

用法(附上單元測(cè)試)

獲取屬性列表

func testGetPropertyList() {
    if let list = SwiftReflectionTool.propertyList(clazz: Book.self) {
        for dict in list {
            for (value, type) in dict {
                print("\(value) : \(type)")
            }
        }
    } else {
        print("未獲取到任何屬性拷沸!")
    }
}

給靜態(tài)屬性賦值(KVC)

func testSetParams4StaticProperty() {
    print("============== 哥只是條單純的分割線 ==============")
    print("賦值前:count = \(Book.value(forKey: InnerConst.CountKey) ?? "")")
    Book.setValue(2, forKey: InnerConst.CountKey)
    print("賦值后:count = \(Book.value(forKey: InnerConst.CountKey) ?? "")")
}

給實(shí)例屬性賦值

func testSetParams4InstanceProperty() {
    print("============== 哥只是條單純的分割線 ==============")
    let paramsDict = [
        "title": "XXX從入門到放棄",
        "author": "cy",
        "numberOfPages": 250,
        "released": "20170707",
        "isSaled": true
    ] as [String : Any]
    let book = Book(title: "", author: "", numberOfPages: 0, released: Date(), isSaled: false)
    print("賦值前:")
    printProperties(obj: book)
    SwiftReflectionTool.setParams(paramsDict, for: book) { 
        print("賦值完成")
    }
    print("賦值后:")
    printProperties(obj: book)
}
// 打印屬性
private func printProperties(obj: NSObject) {
    if let list = SwiftReflectionTool.propertyList(clazz: type(of: obj)) {
        for dict in list {
            for (value, _) in dict {
                print("\(value) : \(obj.value(forKey: value) ?? "")")
            }
        }
    } else {
        print("未獲取到任何屬性色查!")
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撞芍,隨后出現(xiàn)的幾起案子秧了,更是在濱河造成了極大的恐慌,老刑警劉巖序无,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件验毡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡帝嗡,警方通過查閱死者的電腦和手機(jī)晶通,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哟玷,“玉大人狮辽,你說我怎么就攤上這事〕补眩” “怎么了喉脖?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)抑月。 經(jīng)常有香客問我树叽,道長(zhǎng),這世上最難降的妖魔是什么爪幻? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任菱皆,我火速辦了婚禮,結(jié)果婚禮上挨稿,老公的妹妹穿的比我還像新娘仇轻。我一直安慰自己,他們只是感情好奶甘,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布篷店。 她就那樣靜靜地躺著,像睡著了一般臭家。 火紅的嫁衣襯著肌膚如雪疲陕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天钉赁,我揣著相機(jī)與錄音蹄殃,去河邊找鬼。 笑死你踩,一個(gè)胖子當(dāng)著我的面吹牛诅岩,可吹牛的內(nèi)容都是我干的讳苦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼吩谦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鸳谜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起式廷,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤咐扭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后滑废,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝗肪,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年蠕趁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了穗慕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妻导,死狀恐怖逛绵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倔韭,我是刑警寧澤术浪,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站寿酌,受9級(jí)特大地震影響胰苏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜醇疼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一硕并、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧秧荆,春花似錦倔毙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至颁股,卻和暖如春么库,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背甘有。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工诉儒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亏掀。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓忱反,卻偏偏與公主長(zhǎng)得像运准,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缭受,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件该互、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,024評(píng)論 4 62
  • 2015年六月十九日米者,天氣晴。 和往常一樣宇智,我早上七點(diǎn)多起床蔓搞,然后同寢室的另一位姑娘,去了食堂解決早餐随橘。 其實(shí)吧喂分,...
    我是陳姑娘閱讀 492評(píng)論 0 0
  • Android >= 4.1 (API 16) iOS >= 7.0
    Loki_閱讀 4,195評(píng)論 0 0
  • 大家好蒲祈,我是一名攝影室工作者,每天只想定時(shí)分享我們的照片
    進(jìn)擊的小牛牛閱讀 140評(píng)論 0 0
  • 詩(shī)的好處萝嘁,簡(jiǎn)短梆掸。 短到每吐一口煙, 你吸入肺的我看不見牙言。 詩(shī)的好處酸钦,共枕。 你在深夜哭泣時(shí)咱枉, 我搬被子靠你身邊卑硫。 ...
    鋤風(fēng)少年閱讀 516評(píng)論 4 4