KVC

原理模拟

向属性的setter发消息

extension NSObject {
    func value(forKey key: String) -> Any? {
        perform(Selector(key)).takeUnretainedValue()
    }
    
    func setValue(_ value: Any?, forKey key: String) {
        perform(Selector("set\(key.capitalized):"), with: value)
    }
}

Swift解决方案

根据ABI直接读写内存

protocol KVC {}

extension KVC {
    var propOffsetDict: [String: Int] {
        var dict: [String: Int] = [:]
        let metaAddr = unsafeBitCast(Self.self, to: Int.self)
        let typeDescriptorAddr = UnsafePointer<Int>(bitPattern: metaAddr + 64)!.pointee
        let numberOfFields = Int(UnsafePointer<Int32>(bitPattern: typeDescriptorAddr + 36)!.pointee)
        
        let fieldOffsets = {
            let offsetToTheFieldOffsetVector = Int(UnsafePointer<Int32>(bitPattern: typeDescriptorAddr + 40)!.pointee)
            let fieldOffsetsAddr = metaAddr + 8 * offsetToTheFieldOffsetVector
            return [Int](UnsafeBufferPointer(start: UnsafePointer<Int>(bitPattern: fieldOffsetsAddr), count: numberOfFields))
        }()
        
        let fieldNames = {
            let fieldDescriptorOffsetAddr = typeDescriptorAddr + 16
            let fieldDescriptorOffset = Int(UnsafePointer<Int32>(bitPattern: fieldDescriptorOffsetAddr)!.pointee)
            let fieldDescriptorAddr = fieldDescriptorOffsetAddr + fieldDescriptorOffset
            let fieldsAddr = fieldDescriptorAddr + 16
            
            return (0..<numberOfFields).map { i in
                let fieldAddr = fieldsAddr + i * 12
                let fieldNameOffsetAddr = fieldAddr + 8
                let fieldNameOffset = Int(UnsafePointer<Int32>(bitPattern: fieldNameOffsetAddr)!.pointee)
                let fieldNameAddr = fieldNameOffsetAddr + fieldNameOffset
                return String(cString: UnsafePointer<CChar>(bitPattern: fieldNameAddr)!)
            }
        }()
        
        for i in 0..<numberOfFields {
            dict[fieldNames[i]] = fieldOffsets[i]
        }
        return dict
    }
    
    var addr: Int {
        unsafeBitCast(self, to: Int.self)
    }
    
    func setValue<Value>(_ value: Value, forKey key: String) {
        let propAddr = addr + propOffsetDict[key]!
        let propPtr = UnsafeMutablePointer<Value>(bitPattern: propAddr)!
        propPtr.pointee = value
    }
    
    func value<Value>(forKey key: String) -> Value? {
        let propAddr = addr + propOffsetDict[key]!
        let propPtr = UnsafePointer<Value>(bitPattern: propAddr)!
        return propPtr.pointee
    }
}