前言
熟悉 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í)例的屬性
- 無法獲取寫的有
get
或set
方法的屬性
方式二: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("未獲取到任何屬性色查!")
}
}