美文网首页
深度探究HandyJSON(六) KVC

深度探究HandyJSON(六) KVC

作者: Lin__Chuan | 来源:发表于2018-12-27 00:24 被阅读19次

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

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

    相关文章

      网友评论

          本文标题:深度探究HandyJSON(六) KVC

          本文链接:https://www.haomeiwen.com/subject/yqkklqtx.html