书接上文. 这篇文章的代码依然是我根据实际情况将功能进行拆分的代码.
既然我们已经能够为属性在内存中赋值, 那么 KVC 中的 setValue
已经完成了. 我们来看看代码实现.
第 1 步: 获取包装好的属性列表
func properties(_ type: Any.Type) -> [PropertyDescription] {
let hashedType = HashedType(type)
if let properties = cachedProperties[hashedType] { // 有缓存直接取值
return properties
} else { // 取得包装好的属性, 并设置缓存
let properties = propertyDescriptions
cachedProperties[hashedType] = properties
return properties
}
}
// property 缓存
struct HashedType : Hashable {
let hashValue: Int
init(_ type: Any.Type) {
hashValue = unsafeBitCast(type, to: Int.self)
}
init<T>(_ pointer: UnsafePointer<T>) {
hashValue = pointer.hashValue
}
}
func == (lhs: HashedType, rhs: HashedType) -> Bool {
return lhs.hashValue == rhs.hashValue
}
var cachedProperties = [HashedType : Array<PropertyDescription>]()
我们可以通过 Type 来获取属性列表, 这里通过哈希表做了缓存, 避免同一类型的属性重复获取属性, 算是一种优化.
第 2 步: 为属性对应的内存赋值 - 工具类
extension AnyExtensions {
// 获取值
public static func value(from storage: UnsafeRawPointer) -> Any {
return storage.assumingMemoryBound(to: self).pointee
}
}
func extensions(of value: Any) -> AnyExtensions {
struct Extensions : AnyExtensions {}
var extensions: AnyExtensions = Extensions()
withUnsafePointer(to: &extensions) { pointer in
UnsafeMutableRawPointer(mutating: pointer).assumingMemoryBound(to: Any.self).pointee = value
}
return extensions
}
第 3 步: 最终赋值
func set<T>(_ value: Any, key: String, for instance: inout T) {
if let property = properties(T.self).first(where: { $0.key == key }) {
let propAddr = rawPointer.advanced(by: property.offset)
extensions(of: property.type).write(value, to: propAddr)
}
}
接下来主要思考的是怎么获取到值. getValue
有两种思路:
- 利用反射机制, 通过 Mirror 来获取到属性值.
- 直接从内存中取值.
第一种方式比较简单, 但是和我们之前的代码相差比较大, 我们完全可以和之前的代码进行统一, 直接从内存中取值.
第 1 步: 将实例的所有属性名字和内容进行包装
struct Property {
let key: String
let value: Any
}
func properties(_ instance: Any) -> [Property] {
let props = properties(type(of: instance))
return props.map {
let propAddr = rawPointer.advanced(by: $0.offset)
let value = extensions(of: $0.type).value(from: propAddr)
return Property(key: $0.key, value: value)
}
}
第 2 步: 通过包装属性内容的数组, 查找目标属性名, 得出结果
func get(_ key: String, from instance: Any) -> Any {
let any = properties(instance).first(where: { $0.key == key })?.value
guard let value = any else {
return 0
}
return value
}
测试
struct Person {
var isBoy: Bool = true
var age: Int = 0
var height: Double = 130.1
var name: String = "jack"
}
class PersonClass {
var isBoy: Bool = true
var age: Int = 0
var height: Double = 130.1
var name: String = "jack"
}
// 测试 KVC
set(200, key: "age", for: &personClass)
print(personClass.age) // 设置成功
print(get("age", from: personClass))
set(200, key: "age", for: &personStruct)
print(personStruct) // 设置成功
print(get("age", from: personStruct))
或许这里的代码并不健壮, 但可以作为 Swift 中的 KVC 的一种参考.
网友评论