您的当前位置:首页正文

深度探究HandyJSON(六) KVC

来源:花图问答

书接上文. 这篇文章的代码依然是我根据实际情况将功能进行拆分的代码.

既然我们已经能够为属性在内存中赋值, 那么 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 的一种参考.