深度探究HandyJSON(四) 解析 struct

2018-12-25  本文已影响47人  Lin__Chuan

在这个系列的前两篇文章中, 我们讲了 Swift指针的使用, Mirror 的原理, 这些其实都是为接下来的几篇文章做铺垫. 这个系列我并不打算将 HandJSON 的每个细节都讲到, 主要围绕如何通过 strcut / class 对象实现反序列化, 最后实现一个 Swift 版的 KVC.

在这篇文章里, 我们将主要关注 struct 对象实现反序列化.

在开始之前, 先梳理一下 HandJSON 的结构.


HandyJSON大致结构图

HandyJSON 的初代版本和 Reflection 相似, 如果你也对反射感兴趣, 可以去看一下这个项目.

回顾一下, 如何在内存上为实例的属性赋值呢?

那么 HandyJSON 内部是怎么处理的呢?

废话少说, 直接上代码, 我将 HandyJSON 中的代码做了最简化处理.

第 0 步: 定义 Model

struct Person {
    var isBoy: Bool = true
    var age: Int = 0
    var height: Double = 130.1
    var name: String = "jack"
}

第 1 步: 获取属性数量, 以及属性偏移矢量

struct _StructContextDescriptor {
    var flags: Int32
    var parent: Int32
    var mangledName: Int32
    var fieldTypesAccessor: Int32
    var numberOfFields: Int32
    var fieldOffsetVector: Int32
}

var personType = Person.self as Any.Type

// 类型转化 
let pointer = unsafeBitCast(personType, to: UnsafePointer<Int>.self)

let base = pointer.advanced(by: 1)  // contextDescriptorOffsetLocation

// 相对指针偏移值
let relativePointerOffset = base.pointee - Int(bitPattern: base)

以 _StructContextDescriptor 类型访问数据
let descriptor = UnsafeRawPointer(base).advanced(by: relativePointerOffset).assumingMemoryBound(to: _StructContextDescriptor.self)

print(descriptor.pointee)
// _StructContextDescriptor(flags: 262225, parent: -24, mangledName: -16, fieldTypesAccessor: 54167296, numberOfFields: 4, fieldOffsetVector: 2)
// 所有上下文描述符的基类。
struct TargetContextDescriptor {
    // 描述上下文的标志,包括其种类和格式版本
    ContextDescriptorFlags Flags;
    
    // 父上下文,如果这是顶级上下文,则为null
    RelativeContextPointer<Runtime> Parent;

}

struct TargetExtensionContextDescriptor final
    : TargetContextDescriptor<Runtime> {
    
    RelativeDirectPointer<const char> ExtendedContext;
    
    // MangledName
  StringRef getMangledExtendedContext() const {
    return Demangle::makeSymbolicMangledNameStringRef(ExtendedContext.get());
  }
        
}

class TargetTypeContextDescriptor
    : public TargetContextDescriptor<Runtime> {
    
    // type 的名字
    TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name;
    
    int32_t getGenericArgumentOffset() const;
    
    const TargetMetadata<Runtime> * const *getGenericArguments(
                               const TargetMetadata<Runtime> *metadata) const { }
}

第 2 步: 获取属性内存偏移值

extension UnsafePointer {
    init<T>(_ pointer: UnsafePointer<T>) {
        self = UnsafeRawPointer(pointer).assumingMemoryBound(to: Pointee.self)
    }
}

let contextDescriptor = descriptor.pointee
let numberOfFields = Int(contextDescriptor.numberOfFields)
let fieldOffsetVector = Int(contextDescriptor.fieldOffsetVector)

// 成员变量的偏移值
let fieldOffsets = (0..<numberOfFields).map {
    return Int(UnsafePointer<Int32>(pointer)[fieldOffsetVector * 2 + $0])
}
print(fieldOffsets)
// [0, 8, 16, 24]

第 3 步: 获取属性名字和类型并进行包装

struct PropertyDescription {
    public let key: String
    public let type: Any.Type
    public let offset: Int
}

// 类对象
let selfType = unsafeBitCast(pointer, to: Any.Type.self)  // Person

// 属性的包装
var propertyDescriptions: [PropertyDescription] = []

class NameAndType {
    var name: String?
    var type: Any.Type?
}

// 下面这是编译器特性
// 可跳过桥接文件和.h头文件与C代码交互
@_silgen_name("swift_getFieldAt")
func _getFieldAt(
    _ type: Any.Type,
    _ index: Int,
    _ callback: @convention(c) (UnsafePointer<CChar>, UnsafeRawPointer, UnsafeMutableRawPointer) -> Void,
    _ ctx: UnsafeMutableRawPointer
)

for i in 0..<numberOfFields {
    // 属性name, type 的包装类
    var nameAndType = NameAndType()
    
    // 获取属性name, type
    _getFieldAt(selfType, i, { (namePointer, typePointer, nameTypePointer) in
        let name = String(cString: namePointer)
        let type = unsafeBitCast(typePointer, to: Any.Type.self)
        let nameType = nameTypePointer.assumingMemoryBound(to: NameAndType.self).pointee
        nameType.name = name
        nameType.type = type
    }, &nameAndType)
    
    // 将name , type, offset进行包装
    if let name = nameAndType.name, let type = nameAndType.type {
        propertyDescriptions.append(PropertyDescription(key: name, type: type, offset: fieldOffsets[i]))
    }
}

print(propertyDescriptions)

testAdd.c 文件中, 定义如下方法.

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}
int mul(int a, int b) {
    return a * b;
}

testAdd.Swift 中要使用 testAdd.c 中的 add, mul 方法, 我们可以这么做.

@_silgen_name("add")
func c_add(i:Int32,j:Int32)->Int32
@_silgen_name("mul")
func c_mul(i:Int32,times:Int32)->Int32

extension ViewController {
    
    // 不使用桥接文件或者.h文件直接调用.c 文件的函数
    func testCBridge(){
        print(c_add(i: 10, j: 20))  // 30
        print(c_mul(i: 10, times: 20)) // 200
    }
}

第 4 步: 将 JSON 数据进行解析, 反序列化到实例

// 获取头指针
func headPointerOfStruct<T>(instance: inout T) -> UnsafeMutablePointer<Int8> {
    return withUnsafeMutablePointer(to: &instance) {
        return UnsafeMutableRawPointer($0).bindMemory(to: Int8.self, capacity: MemoryLayout<T>.stride)
    }
}

// 获取头指针
var personStruct = Person()
let rawPointer = headPointerOfStruct(instance: &personStruct)

// 获取数据
let dict: [String: Any] = ["isBoy": true, "name": "lili", "age": 18, "height": 100.123]

// 遍历属性
for property in propertyDescriptions {
    let propAddr = rawPointer.advanced(by: property.offset)
    
    if let rawValue = dict[property.key] {
        extensions(of: property.type).write(rawValue, to: propAddr)
    }
}
print("\n person \n", personStruct)
// Person(isBoy: true, age: 18, height: 100.123, name: "lili")
// 写入数据成功

protocol AnyExtensions {}

extension AnyExtensions {
    public static func write(_ value: Any, to storage: UnsafeMutableRawPointer) {
        guard let this = value as? Self else {
            print("类型转换失败, \(type(of: value))无法转为\(Self.self)")
            
            return
        }
        storage.assumingMemoryBound(to: self).pointee = this
    }
}
func extensions(of type: Any.Type) -> AnyExtensions.Type {
    struct Extensions : AnyExtensions {}
    var extensions: AnyExtensions.Type = Extensions.self
    
    withUnsafePointer(to: &extensions) { pointer in
        UnsafeMutableRawPointer(mutating: pointer).assumingMemoryBound(to: Any.Type.self).pointee = type
    }
    return extensions
}

这段代码有一个位置比较有意思, 在第一篇 Swift中指针的使用 这一篇文章中我们就提到

// 将内存临时重新绑定到其他类型进行访问.
let namePtr = pStructHeadRawP.advanced(by: offset).assumingMemoryBound(to: String.self)

// 设置属性值
namePtr.pointee = "lily"

在本例子中, 我们可以直接采用下面这种方式赋值, 但问题是我们从 JSON 数据中获取到的值是Any类型的, 在这其中必须将其转化为对应属性类型, 如果手动转就比较麻烦了.

propAddr.assumingMemoryBound(to: String.self).pointee = rawValue as! String

文中将这个类型直接传给第三方来处理, 通过判断传入的数据类型与属性的类型是否匹配, 来进行赋值, 无需强转数据类型, 这就比较方便了.

上一篇下一篇

猜你喜欢

热点阅读