HandyJSON實現方案淺析

dearmiku發表於2018-04-10

簡述

近日,由於Swift升級,導致一段時間HandyJSON無法使用,借這個機會將HandyJSON好好學習了一下~ 然後從另一種方式實現了類似OC的KVC效果的一個小Demo,通過本文記錄一些自己的收穫,旨在拋磚引玉,有錯誤的地方還請多多指正 ~ (๑•ᴗ•๑)

好了,下面先從Swift的型別結構開始談起吧~

MetaDate

Swift在執行時為程式中的每個型別都保留了後設資料用於記錄(類比OC的元類),包括每個泛型型別的例項.這些資料都是由編譯器靜態生成,且每種型別都有唯一的後設資料記錄.後設資料在執行時根據需要進行延遲建立~ MetaData的資訊 就儲存在類別指標的第一個位元組中.

Swift將MetaData做以下分類~

HandyJSON實現方案淺析

現在主要關注 ClassStruct中, 先說Class 從上面文件中的描述 我們可知 所有Apple平臺的Swift的類都要與OC的類互動,也就是說 Swift的類別指標就是isa指標.

所以SwiftOCClass 在本質上是一樣,只不過Swift剔除其中的動態特性~ 這也是SwiftOC間可以無縫互動的原因之一 ~

上面的內容是Swift Github的文件,大家感興趣可以看看~ Type MetaData文件接下來 我先分析下HandyJSON的實現方式,然後再說說我自己的實現方式.

HandyJSON

實現思路

HandyJSON的實現的內容大概是這樣的:JSON轉字典,同時處理自定義對映的內容 --> 獲取物件/結構體 屬性的記憶體位置,然後將值寫入~ 而其中最核心的內容就是 獲取物件/結構體的屬性列表與偏移量.

HandyJSON是通過後設資料的Nominal Type Descriptor來獲取的~ 這是定義的結構體

struct _NominalTypeDescriptor {
    var mangledName: Int32
    var numberOfFields: Int32
    var fieldOffsetVector: Int32
    var fieldNames: Int32
    var fieldTypesAccessor: Int32
}
複製程式碼

這個結構體中就包含了全部需要的內容,只要得到它,接下來就是將內容寫入和封裝的事了~ 下面就是獲取**_NominalTypeDescriptor**的程式碼,

    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原始碼吧~ 接下來是我的實現思路

我的實現方式

我寫了個Demo 實現的效果大概如下,還有不少瑕疵需要改進,但基本做到KVC的效果了~

HandyJSON實現方案淺析

Class

對於Class我的思路是這樣的,由文件可知,我們能獲得isa指標,接下來要做的就是根據OC runtime原始碼,實現其中需要的型別,然後通過指標進行型別強制轉換,這樣就得到 我們需要的內容了~

但是 我在實現這塊的時候 出現了一些問題 對於ivar結構體時,獲取的內容與runtime中的有差異,而且只能獲取 屬性名稱和偏移量(主要這裡有些困惑,若有懂的老鐵 還請指點一下~)

    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連結,還有一些瑕疵~後續我將其改進,若感覺對你有幫助的話 可以點個Star (๑•ᴗ•๑)

相關文章