DearMiku

HandyJSON实现方案浅析

字数统计: 1.1k阅读时长: 4 min
2018/04/10 Share

HandyJSON实现方案浅析

简述

近日,由于Swift升级,导致一段时间HandyJSON无法使用,借这个机会将HandyJSON好好学习了一下~ 然后从另一种方式实现了类似OC的KVC效果的一个小Demo,通过本文记录一些自己的收获,旨在抛砖引玉,有错误的地方还请多多指正 ~ (๑•ᴗ•๑)

好了,下面先从Swift的类型结构开始谈起吧~

MetaDate

Swift在运行时为程序中的每个类型都保留了元数据用于记录(类比OC的元类),包括每个泛型类型的实例.这些数据都是由编译器静态生成,且每种类型都有唯一的元数据记录.元数据在运行时根据需要进行延迟创建~ MetaData的信息 就存储在类别指针的第一个字节中.

Swift将MetaData做以下分类~

screenshot

现在主要关注 ClassStruct中, 先说Class 从上面文档中的描述 我们可知 所有Apple平台的Swift的类都要与OC的类交互,也就是说 Swift的类别指针就是isa指针.

所以SwiftOCClass 在本质上是一样,只不过Swift剔除其中的动态特性~ 这也是SwiftOC间可以无缝交互的原因之一 ~

上面的内容是Swift Github的文档,大家感兴趣可以看看~ Type MetaData文档接下来 我先分析下HandyJSON的实现方式,然后再说说我自己的实现方式.

HandyJSON

实现思路

HandyJSON的实现的内容大概是这样的:JSON转字典,同时处理自定义映射的内容 –> 获取对象/结构体 属性的内存位置,然后将值写入~ 而其中最核心的内容就是 获取对象/结构体的属性列表与偏移量.

HandyJSON是通过元数据的Nominal Type Descriptor来获取的~ 这是定义的结构体

1
2
3
4
5
6
7
struct _NominalTypeDescriptor {
var mangledName: Int32
var numberOfFields: Int32
var fieldOffsetVector: Int32
var fieldNames: Int32
var fieldTypesAccessor: Int32
}

这个结构体中就包含了全部需要的内容,只要得到它,接下来就是将内容写入和封装的事了~ 下面就是获取_NominalTypeDescriptor的代码,

1
2
3
4
5
6
7
8
9
10
11
12
13
    var nominalTypeDescriptor: NominalTypeDescriptor? {
let pointer = UnsafePointer<Int>(self.pointer)
let base = pointer.advanced(by: nominalTypeDescriptorOffsetLocation)
if base.pointee == 0 {
// swift class created dynamically in objc-runtime didn't have valid nominalTypeDescriptor
return nil
}
#if swift(>=4.1) || (swift(>=3.3) && !swift(>=4.0))
return NominalTypeDescriptor(pointer: relativePointer(base: base, offset: base.pointee - base.hashValue))
#else
return NominalTypeDescriptor(pointer: relativePointer(base: base, offset: base.pointee))
#endif
}

这部分内容也是在Swift更新后出问题的地方.在文档中关于这里的部分表示Warning: this is all out of date!,可能作者是通过Swift源码分享确定_NominalTypeDescriptor的位置的吧~

我这里就是简单介绍一下,详细的内容 大家还是去看HandyJSON源码吧~ 接下来是我的实现思路

我的实现方式

我实现的效果大概如下,还有不少瑕疵需要改进,但基本做到KVC的效果了~
screenshot

Class

对于Class我的思路是这样的,由文档可知,我们能获得isa指针,接下来要做的就是根据OC runtime源码,实现其中需要的类型,然后通过指针进行类型强制转换,这样就得到 我们需要的内容了~

但是 我在实现这块的时候 出现了一些问题 对于ivar结构体时,获取的内容与runtime中的有差异,而且只能获取 属性名称和偏移量(主要这里有些困惑,若有懂的老铁 还请指点一下~)

1
2
3
4
5
6
7
8
9
10
11
12
struct MK_ivar {

var mask1:Int32

var off:UnsafePointer<CChar>

var name:UnsafePointer<CChar>

var mask2:UInt32
var mask3:UInt32

}

关于属性类型 我则是通过Mirror来获取的,这样就得到需要的全部信息了.

Struct

Struct则很简单了,在Mirror中获取属性的顺序和结构体中分部的属性是一样的,通Mirror获取属性类型,然后推断出它在结构体中填充的大小,这样各个属性的偏移量就得到了,接下来只要将Value写入就好了~

总结

这是我写的小Demo,简单实现了KVC的效果,还有一些瑕疵~后续我可能会将它封装成库玩一玩,若感觉对你有帮助的话 可以点个Star (๑•ᴗ•๑)

CATALOG
  1. 1. HandyJSON实现方案浅析
  2. 2. 简述
  3. 3. MetaDate
  4. 4. HandyJSON
    1. 4.1. 实现思路
  5. 5. 我的实现方式
    1. 5.1. Class
    2. 5.2. Struct
  6. 6. 总结