Swift-Reflection
2017-07-07 本文已影响232人
chernyog
前言
熟悉 Java 的读者可能会知道反射 (Reflection)。这是一种在运行时检测、访问或者修改类型的行为的特性。一般的静态语言类型的结构和方法的调用等都需要在编译时决定,开发者能做的很多时候只是使用控制流 (比如 if 或者 switch) 来决定做出怎样的设置或是调用哪个方法。而反射特性可以让我们有机会在运行的时候通过某些条件实时地决定调用的方法,或者甚至向某个类型动态地设置甚至加入属性及方法,是一种非常灵活和强大的语言特性。
Objective-C 中我们不太会经常提及到 “反射” 这样的词语,因为 Objective-C 的运行时比一般的反射还要灵活和强大。可能很多读者已经习以为常的像是通过字符串生成类或者 selector,并且进而生成对象或者调用方法等,其实都是反射的具体的表现。而在 Swift 中其实就算抛开 Objective-C 的运行时的部分,在纯 Swift 范畴内也存在有反射相关的一些内容,只不过相对来说功能要弱得多。
摘抄自《Swifter: 100个Swift开发必备 Tip》
需求
- 获取类的属性列表
- 给属性赋值
Swift 反射的实现方式
- Mirror
- OC runtime
方式一:Mirror
实现方式
获取属性的类型
func getTypeOfProperty (_ name: String) -> Any.Type {
// 注意:self是实例(对象),如果是类,则无法获取其属性
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)]参数")
}else if …… {
// ……
}else {
obj.setValue(value, forKey: key)
}
}
}
}
特点
- 简单(Swift 中所有的类型都实现了 _Reflectable)
- 无法获取类型的属性,只能获取实例的属性
- 无法获取写的有
get
或set
方法的属性
方式二:OC runtime
实现方式(部分代码)
获取属性的类型
/// 获取对象属性类型列表
/// - 注意:这种方式获取属性,对于非引用类型的属性,必须有初始值,否则无法获取到!
/// - 参考自:https://github.com/Sajjon/SwiftReflection
///
/// - parameter clazz: 对象类型
///
/// - returns: 属性名 & 类型 字典数组.
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
}
给属性赋值
/// 给对象赋值
///
/// - parameter paramsDict: 参数字典
/// - parameter obj: 待赋值的对象
/// - parameter complete: 赋值完成的回调
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对应的类型
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)]参数")
}
}
print("================= 赋值完成 =================")
complete()
}
}
特点
- 功能强大,可作用于任何类或者实例
- 比较麻烦,需要有 Objective-C 的基础
- 无法获取没有初始化的非引用类型属性(值类型必须得初始化!)
- 所作用的对象,必须继承自NSObject
用法(附上单元测试)
获取属性列表
func testGetPropertyList() {
if let list = SwiftReflectionTool.propertyList(clazz: Book.self) {
for dict in list {
for (value, type) in dict {
print("\(value) : \(type)")
}
}
} else {
print("未获取到任何属性!")
}
}
给静态属性赋值(KVC)
func testSetParams4StaticProperty() {
print("============== 哥只是条单纯的分割线 ==============")
print("赋值前:count = \(Book.value(forKey: InnerConst.CountKey) ?? "")")
Book.setValue(2, forKey: InnerConst.CountKey)
print("赋值后:count = \(Book.value(forKey: InnerConst.CountKey) ?? "")")
}
给实例属性赋值
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("未获取到任何属性!")
}
}