深度探究HandyJSON(二) Mirror 的原理

在上一篇文章中, 我著重介紹了 Swift中指針的使用, 這篇文章主要圍繞以下幾點(diǎn):

  1. HandyJSON 的優(yōu)勢(shì).
  2. HandyJSON 解析數(shù)據(jù)的原理.
  3. Mirror 的原理.

HandyJSON 的優(yōu)勢(shì)

JSON(JavaScript Object Notation) 是一種輕量級(jí)的數(shù)據(jù)交換格式, 應(yīng)用廣泛. 在 App 的使用過(guò)程中, 服務(wù)端給移動(dòng)端發(fā)送的大部分都是 JSON 數(shù)據(jù), 移動(dòng)端需要解析數(shù)據(jù)才能做進(jìn)一步的處理. 在解析JSON數(shù)據(jù)這一塊, 目前 Swift 中流行的框架基本上是 SwiftyJSON, ObjectMapper, JSONNeverDie, HandyJSON 這么幾種.

我們應(yīng)該如何選擇呢?

首先我們應(yīng)該先明白解析 JSON 的原理. 目前有兩個(gè)方向.

  • 保持 JSON 語(yǔ)義, 直接解析 JSON.
    SwiftyJSON 就是這樣的. 本質(zhì)上仍然需要根據(jù) JSON 結(jié)構(gòu)去取值.

  • 預(yù)定義 Model 類(lèi), 將 JSON 反序列化類(lèi)的實(shí)例, 再使用這些實(shí)例.
    這種方式和 OC 中的 MJExtension 的思路是一致的. 在 Swift 中, ObjectMapper, JSONNeverDie, 以及 HandyJSON 做的都是將 JSON 文本反序列化到 Model 類(lèi)上.

第二種思路是我們熟悉和比較方便的. 和服務(wù)端定義好數(shù)據(jù)結(jié)構(gòu), 寫(xiě)好 Model 就可以直接解析.

第二種思路有三種實(shí)現(xiàn)方式:

  1. 完全沿用 OC 中的方式, 讓 Model 類(lèi)繼承自 NSObject, 通過(guò) class_copyPropertyList 方法拿到 Model 的各種屬性, 從 JSON 中拿到對(duì)應(yīng)的 value, 再通過(guò) OC 中 利用runtime 機(jī)制 實(shí)現(xiàn)的 KVC 方法為屬性賦值. 如 JSONNeverDie.
  2. 支持純 Swift 類(lèi), 但要求開(kāi)發(fā)者實(shí)現(xiàn) mapping 函數(shù), 使用重載的運(yùn)算符進(jìn)行賦值, 如 ObjectMapper.
  3. 獲取到 JSON 數(shù)據(jù)后, 直接在內(nèi)存中為實(shí)例的屬性賦值. HandyJSON 就是這樣實(shí)現(xiàn)的.
  • 第一種方式的缺點(diǎn)在于需要強(qiáng)制繼承 NSObject, 這不適用于 struct 定義的 Model. 因?yàn)?struct 創(chuàng)建的 Model 不能通過(guò) KVC 為其賦值.
  • 第二種方式的缺點(diǎn)在于自定義 mapping 函數(shù), 維護(hù)比較困難.
  • 第三種方式在使用上和 MJExtension 基本差不多, 比較方便. 是我們所推薦的.

HandyJSON 解析數(shù)據(jù)的原理.

如何在內(nèi)存上為實(shí)例的屬性賦值呢?

在上一篇文章里, 我們介紹了 struct 實(shí)例 和 class 實(shí)例在內(nèi)存上結(jié)構(gòu)的不同. 為屬性賦值, 我們需要以下步驟:

  1. 獲取到屬性的名稱(chēng)和類(lèi)型.
  2. 找到實(shí)例在內(nèi)存中的 headPointer, 通過(guò)屬性的類(lèi)型計(jì)算內(nèi)存中的偏移值, 確定屬性在內(nèi)存中的位置.
  3. 在內(nèi)存中為屬性賦值.

在 Swift 中實(shí)現(xiàn)反射機(jī)制的類(lèi)是 Mirror, 通過(guò) Mirror 類(lèi)可以獲取到類(lèi)的屬性, 但是不能為屬性賦值, 它是可讀的. 但是我們可以直接在內(nèi)存中為實(shí)例賦值. 這是一種思路. 另外一種思路是不利用 Mirror, 直接在內(nèi)存中獲取到屬性的名稱(chēng)和類(lèi)型, 這也是可以的. 目前 HandyJSON 就是利用的第二種方式.

Mirror 的原理

雖然 HandyJSON 的核心實(shí)現(xiàn)上并沒(méi)有依賴(lài)于 Mirror, Mirror 的性能不好. 但是這個(gè)類(lèi)還是挺有意思的. 如果沒(méi)使用過(guò) Mirror, 看看這篇文章.

class LCPerson {
    var name: String?
}

Mirror(reflecting: LCPerson()).children.forEach { (child) in
    print(child.label ?? "", child.value)
    child.value = "lili" // error胚想,不能直接賦值
}

下面的內(nèi)容極大的借鑒了一個(gè)牛人寫(xiě)的 how-mirror-works, 所以也算是我的學(xué)習(xí)筆記.

要從源碼角度剖析 Mirror, 我們需要拿到 Swift 的源碼, 可以從 Apple 的 官方 github clone 源代碼到本地. 我們需要真正關(guān)注的是 stdlibinclude 這兩個(gè)文件夾, 前者是 Swift 的標(biāo)準(zhǔn)庫(kù)的源碼, 后者是支持文件.

Swift 源碼

反射的 API 有一部分是用 Swift 實(shí)現(xiàn)的, 另一部分是 C++ 實(shí)現(xiàn)的. 分別在 ReflectionMirror.swiftReflectionMirror.mm. 這兩者通過(guò)一小組暴露給 Swift 的 C++ 函數(shù)進(jìn)行通信.
比如在 ReflectionMirror.swift 中的.

@_silgen_name("swift_reflectionMirror_subscript")
internal func _getChild<T>(
  of: T,
  type: Any.Type,
  index: Int,
  outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
  outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
) -> Any

與其在 ReflectionMirror.mm 中的實(shí)現(xiàn)是下面

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
AnyReturn swift_reflectionMirror_subscript(OpaqueValue *value, const Metadata *type,
                                           intptr_t index,
                                           const char **outName,
                                           void (**outFreeFunc)(const char *),
                                           const Metadata *T) {
  return call(value, T, type, [&](ReflectionMirrorImpl *impl) {
    return impl->subscript(index, outName, outFreeFunc);
  });
}

注意:

  • ReflectionMirror.swift 中的 _getChild() 函數(shù)用于獲取對(duì)應(yīng)索引值的子元素信息.
  • @_silgen_name 修飾符會(huì)通知 Swift 編譯器將這個(gè)函數(shù)映射成 swift_reflectionMirror_subscript 符號(hào)疗韵,而不是 Swift 通常對(duì)應(yīng)到的 _getChild 方法名修飾. 也就是說(shuō), 在 Swift 中直接調(diào)用 _getChild 函數(shù), 實(shí)際上就是調(diào)用 C++ 實(shí)現(xiàn)的 swift_reflectionMirror_subscript 函數(shù).
  • SWIFT_CC(swift) 會(huì)告訴編譯器這個(gè)函數(shù)使用的是 Swift 的調(diào)用約定疮装,而不是 C/C++ 的. SWIFT_RUNTIME_STDLIB_INTERFACE 標(biāo)記這是個(gè)函數(shù).
  • C++ 的參數(shù)會(huì)去特意匹配在 Swift 中聲明的函數(shù)調(diào)用. 當(dāng) Swift 調(diào)用 _getChild 時(shí), C++ 會(huì)用包含的 Swift 值指針的 value, 包含類(lèi)型參數(shù)的 type, 包含目標(biāo)索引值的 index, 包含標(biāo)簽信息的 outname, 包含釋放目標(biāo)字符串內(nèi)存的方法 outFreeFunc, 包含類(lèi)型相應(yīng)的范型 <T> 的 T 的函數(shù)參數(shù)來(lái)調(diào)用此函數(shù).

Mirror 的在 Swift 和 C++ 之間的全部接口由以下函數(shù)組成:

獲取標(biāo)準(zhǔn)化類(lèi)型
@_silgen_name("swift_reflectionMirror_normalizedType")
internal func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type

獲取子元素?cái)?shù)量
@_silgen_name("swift_reflectionMirror_count")
internal func _getChildCount<T>(_: T, type: Any.Type) -> Int

獲取子元素信息
@_silgen_name("swift_reflectionMirror_subscript")
internal func _getChild<T>(
  of: T,
  type: Any.Type,
  index: Int,
  outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
  outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
) -> Any

// Returns 'c' (class), 'e' (enum), 's' (struct), 't' (tuple), or '\0' (none)
獲取對(duì)象的展示類(lèi)型
@_silgen_name("swift_reflectionMirror_displayStyle")
internal func _getDisplayStyle<T>(_: T) -> CChar

@_silgen_name("swift_reflectionMirror_quickLookObject")
internal func _getQuickLookObject<T>(_: T) -> AnyObject?

判斷一個(gè)類(lèi)是不是另一個(gè)類(lèi)的子類(lèi), 類(lèi)似于 NSObject 中的 isKindOfClass
@_silgen_name("_swift_stdlib_NSObject_isKindOfClass")
internal func _isImpl(_ object: AnyObject, kindOf: AnyObject) -> Bool

動(dòng)態(tài)派發(fā)

元組、結(jié)構(gòu)楚殿、類(lèi)和枚舉都需要不同的代碼去完成這些繁多的任務(wù),比如說(shuō)查找子元素的數(shù)量, 比如針對(duì) OC, Swift 做不同的處理. 所有的函數(shù)因?yàn)樾枰煌念?lèi)型的檢查而需要派發(fā)不同的實(shí)現(xiàn)代碼.

為了解決這個(gè)問(wèn)題, Swift采用了一種類(lèi)似動(dòng)態(tài)派發(fā)的方式, 利用一個(gè)單獨(dú)的函數(shù)將 Swift 類(lèi)型映射成一個(gè) C++ 類(lèi)的實(shí)例. 在一個(gè)實(shí)例上調(diào)用方法然后派發(fā)合適的實(shí)現(xiàn).

映射的函數(shù)是 call().

template<typename F>
auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedType,
          const F &f) -> decltype(f(nullptr)) { ... }

參數(shù):

  • passedValue 是實(shí)際需要傳入的Swift的值的指針.
  • T 是該值的靜態(tài)類(lèi)型. 對(duì)應(yīng) Swift 中的范型參數(shù) <T>.
  • passedType 是被顯式傳遞進(jìn) Swift 側(cè)并且會(huì)實(shí)際應(yīng)用在反射過(guò)程中的類(lèi)型(這個(gè)類(lèi)型和在使用 Mirror 作為父類(lèi)的實(shí)例在實(shí)際運(yùn)行時(shí)的對(duì)象類(lèi)型不一樣).
  • f 參數(shù)會(huì)傳遞這個(gè)函數(shù)查找到的會(huì)被調(diào)用的實(shí)現(xiàn)的對(duì)象引用.

返回值:
這個(gè)函數(shù)會(huì)返回當(dāng)這個(gè) f 參數(shù)調(diào)用時(shí)的返回值.

call 的內(nèi)部實(shí)現(xiàn) 主要有兩部分:

針對(duì) Swift
 auto call = [&](ReflectionMirrorImpl *impl) { ... }
針對(duì) OC
 auto callClass = [&] { ... }

以及通過(guò) Switch 處理各種不同類(lèi)型的實(shí)現(xiàn).

callClass 內(nèi)部也會(huì)調(diào)用 call, 因?yàn)?call 內(nèi)部用一個(gè) ReflectionMirrorImpl 的子類(lèi)實(shí)例去結(jié)束調(diào)用 f赶盔,然后會(huì)調(diào)用這個(gè)實(shí)例上的方法去讓真正的工作完成.

auto call = [&](ReflectionMirrorImpl *impl) {
    impl->type = type;
    impl->value = value;
    auto result = f(impl);
    SWIFT_CC_PLUSONE_GUARD(T->vw_destroy(passedValue));
    return result;
};

重點(diǎn)關(guān)注下 ReflectionMirrorImpl 的實(shí)現(xiàn).

// Abstract base class for reflection implementations.
struct ReflectionMirrorImpl {
  const Metadata *type;  類(lèi)型信息
  OpaqueValue *value;    值指針
  
  virtual char displayStyle() = 0;  顯示方式
  virtual intptr_t count() = 0;     子元素?cái)?shù)量
  子元素信息
  virtual AnyReturn subscript(intptr_t index, const char **outName,
                              void (**outFreeFunc)(const char *)) = 0;
  virtual const char *enumCaseName() { return nullptr; }

#if SWIFT_OBJC_INTEROP
  virtual id quickLookObject() { return nil; }
#endif
  
  virtual ~ReflectionMirrorImpl() {}
};

關(guān)鍵: 作用在 Swift 和 C++ 組件之間的接口函數(shù)就會(huì)用 call 去調(diào)用相應(yīng)的方法.
比如前面提到的: swift_reflectionMirror_subscript, 獲取子元素信息. 內(nèi)部就會(huì)調(diào)用

call(value, T, type, [&](ReflectionMirrorImpl *impl) {
    return impl->subscript(index, outName, outFreeFunc);
});

大概有以下 5 種:


Tuple, Struct, Enum 等類(lèi)型都有對(duì)應(yīng)的 ReflectionMirrorImpl 的實(shí)現(xiàn).

下面會(huì)依次介紹元組的反射, 結(jié)構(gòu)體的反射, 類(lèi)的反射, 枚舉的反射, 其他種類(lèi).

元組的反射

元組的反射的實(shí)現(xiàn)
總體概覽:

struct TupleImpl : ReflectionMirrorImpl {
    // 顯示方式, 元組是 't'
    char displayStyle() { ... }
 
    // 子元素的數(shù)量
    intptr_t count() { ... }
 
    // 子元素信息
    AnyReturn subscript(intptr_t i, const char **outName,
            void (**outFreeFunc)(const char *)) { ... }
 
 }

接下來(lái)從上往下看:

char displayStyle() {
    return 't';
}

返回 't' 的顯示樣式來(lái)表明這是一個(gè)元組.

intptr_t count() {
    auto *Tuple = static_cast<const TupleTypeMetadata *>(type);
    return Tuple->NumElements;
}

count() 方法返回子元素的數(shù)量. type 由原來(lái)的 MetaType 類(lèi)型的指針轉(zhuǎn)為 TupleTypeMetadata 類(lèi)型的指針. TupleTypeMetadata 類(lèi)型有一個(gè)記錄元組的元素?cái)?shù)量的字段 NumElements, 由此取值.
注意: TupleTypeMetadata 類(lèi)型實(shí)際是 TargetTupleTypeMetadata 類(lèi)型, 這是一個(gè) struct, 內(nèi)部包含字段 NumElements.

using TupleTypeMetadata = TargetTupleTypeMetadata<InProcess>;

subscript() 方法比較復(fù)雜.

auto *Tuple = static_cast<const TupleTypeMetadata *>(type);
  1. 獲取 typeTupleTypeMetadata 類(lèi)型的指針.
if (i < 0 || (size_t)i > Tuple->NumElements)
      swift::crash("Swift mirror subscript bounds check failure");
  1. 防止調(diào)用者請(qǐng)求了不存在的元組的索引.

i 的作用在于 可以檢索元素和對(duì)應(yīng)的名字.

  • 對(duì)于元組而言, 這個(gè)名字是該元素在元組中的label, 若沒(méi)有l(wèi)abel, 默認(rèn)就是一個(gè) .0 的數(shù)值指示器.
  • 對(duì)于結(jié)構(gòu)體或者類(lèi)來(lái)說(shuō), 這個(gè)名字是屬性名.
bool hasLabel = false;
if (const char *labels = Tuple->Labels) {
  const char *space = strchr(labels, ' ');
  for (intptr_t j = 0; j != i && space; ++j) {
    labels = space + 1;
    space = strchr(labels, ' ');
  }

  // If we have a label, create it.
  if (labels && space && labels != space) {
    *outName = strndup(labels, space - labels);
    hasLabel = true;
  }
}
  1. 查找元組中第 i 個(gè)位置的 label. label 是以空格為間隔存儲(chǔ)在 Tuple 中的 labels 字段里.

strchr(s, 'c') 可以查找字符串 s 中首次出現(xiàn)字符 c 的位置.
返回首次出現(xiàn) 'c' 的位置的指針, 返回的地址是被查找字符串指針開(kāi)始的第一個(gè)與 'c' 相同字符的指針.

strndup(labels, space - labels)
將字符串拷貝到新建的位置處, 若不需要返回的字符串, 需要手動(dòng)調(diào)用 free 將其釋放.

if (!hasLabel) {
  // The name is the stringized element number '.0'.
  char *str;
  asprintf(&str, ".%" PRIdPTR, i);
  *outName = str;
}
  1. 如果沒(méi)有 label, 則創(chuàng)建一個(gè)以 .i 為名字的字符串為 label. 類(lèi)似下面這樣
let tuple = ("jack", "lily", "lucy")
print(tuple.0)   // jack
*outFreeFunc = [](const char *str) { free(const_cast<char *>(str)); };
  1. outFreeFunc 用于調(diào)用者手動(dòng)調(diào)用此函數(shù)來(lái)釋放返回的 label. 對(duì)應(yīng)前面的 strndup.

strdup() 在內(nèi)部調(diào)用了 malloc() 為變量分配內(nèi)存, 不需要使用返回的字符串時(shí), 需要用 free() 釋放相應(yīng)的內(nèi)存空間, 否則會(huì)造成內(nèi)存泄漏.

// Get the nth element.
auto &elt = Tuple->getElement(i);
auto *bytes = reinterpret_cast<const char *>(value);
auto *eltData = reinterpret_cast<const OpaqueValue *>(bytes + elt.Offset);
  1. 利用 getElement(i) 獲取 Tuple 元數(shù)據(jù)的相關(guān)信息, elt 是一個(gè) Element 類(lèi)型的結(jié)構(gòu)體實(shí)例.
struct Element {
    /// The type of the element.
    ConstTargetMetadataPointer<Runtime, swift::TargetMetadata> Type;

    /// The offset of the tuple element within the tuple.
    StoredSize Offset;

    OpaqueValue *findIn(OpaqueValue *tuple) const {
      return (OpaqueValue*) (((char*) tuple) + Offset);
    }
};

elt 包含了一個(gè) offset 字段, 表示該元素在元組中的偏移值, 可以應(yīng)用在元組值上, 去獲得元素的值指針.

Any result;

result.Type = elt.Type;
auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer);
result.Type->vw_initializeWithCopy(opaqueValueAddr,
                                   const_cast<OpaqueValue *>(eltData));

  1. elt 還包含了元素的類(lèi)型, 通過(guò)類(lèi)型和值的指針斤程,去構(gòu)造一個(gè)包括這個(gè)值新的 Any 對(duì)象.
return AnyReturn(result);
  1. 通過(guò) AnyReturn 包裝, 返回子元素信息. AnyReturn 是一個(gè) struct. 它可以保證即使在任何將在寄存器中返回 Any 的體系結(jié)構(gòu)中也是如此.
struct AnyReturn {
  Any any;
  AnyReturn(Any a) : any(a) { }
  operator Any() { return any; }
  ~AnyReturn() { }
};

這里的 Any 指的是

/// The layout of Any.
using Any = OpaqueExistentialContainer;

在介紹結(jié)構(gòu)體, 類(lèi), 枚舉的反射時(shí), 先來(lái)看看一個(gè)函數(shù) swift_getFieldAt, 這個(gè)函數(shù)可以通過(guò)用語(yǔ)言的元數(shù)據(jù)去查找類(lèi)型信息. HandyJSON 里面直接用到了.

swift_getFieldAt

swift_getFieldAt() 可以通過(guò)結(jié)構(gòu)、類(lèi)和枚舉的元數(shù)據(jù)去查找類(lèi)型信息

函數(shù)原型:

void swift::swift_getFieldAt(
    const Metadata *base, unsigned index,
    std::function<void(llvm::StringRef name, FieldType fieldInfo)>
        callback) { ... }

接下來(lái)從上往下看.

 auto *baseDesc = base->getTypeContextDescriptor();
 if (!baseDesc)
    return;

通過(guò)元數(shù)據(jù)獲取類(lèi)型的上下文描述

auto getFieldAt = [&](const FieldDescriptor &descriptor) { ... }

定義一個(gè)方法 getFieldAt , 從描述符中查找信息.

接下來(lái)的工作分為兩步.

  1. 查找描述符.
  2. 調(diào)用 getFieldAt 方法, 通過(guò)描述符查找信息.
auto dem = getDemanglerForRuntimeTypeResolution();

獲取符號(hào)還原器, 將符號(hào)修飾過(guò)的類(lèi)名還原為實(shí)際的類(lèi)型引用.

auto &cache = FieldCache.get();

獲取字段描述符的緩存.

auto isRequestedDescriptor = [&](const FieldDescriptor &descriptor) {
    assert(descriptor.hasMangledTypeName());
    auto mangledName = descriptor.getMangledTypeName(0);

    if (!_contextDescriptorMatchesMangling(baseDesc,
                                           dem.demangleType(mangledName)))
      return false;

    cache.FieldCache.getOrInsert(base, &descriptor);
    getFieldAt(descriptor);
    return true;
};

定義一個(gè)方法, 檢查輸入的描述符是否是被需要的哪一個(gè), 如果是, 那么將描述符放到緩存中, 并且調(diào)用 getFieldAt, 然后返回成功給調(diào)用者.

if (auto Value = cache.FieldCache.find(base)) {
    getFieldAt(*Value->getDescription());
    return;
}

如果存在字段描述符緩存, 那么通過(guò) getFieldAt 獲取字段信息.

ScopedLock guard(cache.SectionsLock);
  // Otherwise let's try to find it in one of the sections.
  for (auto &section : cache.DynamicSections) {
    for (const auto *descriptor : section) {
      if (isRequestedDescriptor(*descriptor))
        return;
    }
  }

  for (const auto &section : cache.StaticSections) {
    for (auto &descriptor : section) {
      if (isRequestedDescriptor(descriptor))
        return;
    }
  }

字段描述符可以在運(yùn)行時(shí)注冊(cè), 也可以在編譯時(shí)加入到二進(jìn)制文件中. 這兩個(gè)循環(huán)查找所有已知的字段描述符以進(jìn)行匹配.

接下來(lái)看看 getFieldAt 內(nèi)部的實(shí)現(xiàn)過(guò)程.
getFieldAt 這個(gè)方法作用是將字段描述符轉(zhuǎn)化為名字和字段類(lèi)型, 進(jìn)行回調(diào)返回.

auto &field = descriptor.getFields()[index];
  1. 獲取字段的引用.
auto name = field.getFieldName(0);
  1. 在引用中獲取字段的名字.
if (!field.hasMangledTypeName()) {
      callback(name, FieldType().withIndirect(field.isIndirectCase()));
      return;
}
  1. 判斷是否有類(lèi)型. 比如, 字段實(shí)際上是一個(gè)枚舉, 那么它可能沒(méi)有類(lèi)型.
std::vector<const ContextDescriptor *> descriptorPath;
{
  const auto *parent = reinterpret_cast<
                          const ContextDescriptor *>(baseDesc);
  while (parent) {
    if (parent->isGeneric())
      descriptorPath.push_back(parent);

    parent = parent->Parent.get();
  }
}
  1. 定義一個(gè) ContextDescriptor 類(lèi)型的指針 descriptorPath, 通過(guò) baseSesc 獲取描述符的路徑. 也就是將這個(gè)類(lèi)型的所有范型的上下文抽離出來(lái).
auto typeInfo = _getTypeByMangledName(
        field.getMangledTypeName(0),
        [&](unsigned depth, unsigned index) -> const Metadata * { ... }
  1. 獲取類(lèi)型信息

前面有提到, 字段的引用 field 將字段類(lèi)型儲(chǔ)存為一個(gè)符號(hào)修飾的名字, 但是最終回調(diào)的是元數(shù)據(jù)的指針. 所以需要將符號(hào)修飾的名字轉(zhuǎn)化為一個(gè)真實(shí)的類(lèi)型. (字符串 -> 類(lèi)型)

_getTypeByMangledName 方法的作用在此.

_getTypeByMangledName 內(nèi)部,

field.getMangledTypeName(0)

首先傳入由符號(hào)修飾的類(lèi)型.

if (depth >= descriptorPath.size())
 return nullptr;

接著檢查請(qǐng)求的深度與描述符的路徑, 如果前者比后者大, 返回失敗

unsigned currentDepth = 0;
unsigned flatIndex = index;
const ContextDescriptor *currentContext = descriptorPath.back();

for (const auto *context : llvm::reverse(descriptorPath)) {
    if (currentDepth >= depth)
      break;
    
    flatIndex += context->getNumGenericParams();
    currentContext = context;
    ++currentDepth;
}

接著, 從字段的類(lèi)型中獲取范型參數(shù). 將索引和深度轉(zhuǎn)化為單獨(dú)的扁平化的索引, 通過(guò)遍歷描述符的路徑, 在每個(gè)階段添加范型參數(shù)的數(shù)量直到達(dá)到深度為止.

if (index >= currentContext->getNumGenericParams())
    return nullptr;

如果索引比范型參數(shù)可達(dá)到的深度大乖酬,那么失敗.

return base->getGenericArgs()[flatIndex];

_getTypeByMangledName 的最后, 從元數(shù)據(jù) base 獲得基本類(lèi)型信息, 再在其中獲得合適的范型參數(shù).

callback(name, FieldType()
                       .withType(typeInfo)
                       .withIndirect(field.isIndirectCase())
                       .withWeak(typeInfo.isWeak()));

getFieldAt 方法中最重要的一步, 執(zhí)行回調(diào), 將字段名字和類(lèi)型暴露出來(lái).

結(jié)構(gòu)體的反射

結(jié)構(gòu)體的反射的實(shí)現(xiàn)和元組類(lèi)似, 但是結(jié)構(gòu)體可能包含需要反射代碼去提取的弱引用. 下面是實(shí)現(xiàn)代碼.

struct StructImpl : ReflectionMirrorImpl {
    顯示方式, 結(jié)構(gòu)體是 's'
    char displayStyle() { ... }
    
    子元素?cái)?shù)量
    intptr_t count() { ... }
    
    所有子元素信息
    AnyReturn subscript(intptr_t i, const char **outName,
                      void (**outFreeFunc)(const char *)) { ... }
}
char displayStyle() {
    return 's';
}

結(jié)構(gòu)體的顯示樣式是 's'

intptr_t count() {
    auto *Struct = static_cast<const StructMetadata *>(type);
    return Struct->getDescription()->NumFields;
}

count 方法返回子元素的數(shù)量. type 由原來(lái)的 MetaType 類(lèi)型的指針轉(zhuǎn)為 StructMetadata 類(lèi)型的指針.

StructMetadata 類(lèi)型有一個(gè) TargetStructDescriptor<Runtime> 類(lèi)型的字段 getDescription(), 這是一個(gè)指針, TargetStructDescriptor 類(lèi)中有一個(gè)字段 NumFields, 由此可獲得子元素?cái)?shù)量.

注意: StructMetadata 類(lèi)型實(shí)際是 TargetStructMetadata 類(lèi)型, 這是一個(gè) struct

using StructMetadata = TargetStructMetadata<InProcess>;
AnyReturn subscript(intptr_t i, const char **outName,
                      void (**outFreeFunc)(const char *)) { ... }

subscript 方法比較復(fù)雜

auto *Struct = static_cast<const StructMetadata *>(type);
  1. 獲取 typeStructMetadata 類(lèi)型的指針.
if (i < 0 || (size_t)i > Struct->getDescription()->NumFields)
swift::crash("Swift mirror subscript bounds check failure");
  1. 進(jìn)行邊界檢查, 防止訪問(wèn)不存在的子元素.
auto fieldOffset = Struct->getFieldOffsets()[i];
  1. 查找對(duì)應(yīng)索引的字段偏移值.
Any result;
    
swift_getFieldAt(type, i, [&](llvm::StringRef name, FieldType fieldInfo) {
  assert(!fieldInfo.isIndirect() && "indirect struct fields not implemented");

  獲取字段名字
  *outName = name.data();
  *outFreeFunc = nullptr;
  
  計(jì)算字段儲(chǔ)存的指針
  auto *bytes = reinterpret_cast<char*>(value);
  auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);
  
  loadSpecialReferenceStorage 方法用于處理將字段的值復(fù)制到 Any 返回值以處理弱引用
  bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result);
  
  如果值沒(méi)有被載入的話那么那個(gè)值用普通的儲(chǔ)存死相,并且以普通的方式拷貝到返回值
  if (!didLoad) {
    result.Type = fieldInfo.getType();
    auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer);
    result.Type->vw_initializeWithCopy(opaqueValueAddr,
                                       const_cast<OpaqueValue *>(fieldData));
  }
});
  1. 通過(guò) _swift_getFieldAt 方法, 獲取結(jié)構(gòu)體字段中的信息(字段的名字和類(lèi)型).
AnyReturn(result);

最后, 將子元素信息返回.

類(lèi)的反射

struct ClassImpl : ReflectionMirrorImpl {
    顯示樣式, 類(lèi)是 'c'
    char displayStyle() { ... } 
    
    子元素?cái)?shù)量
    intptr_t count() { ... }
    
    子元素信息
    AnyReturn subscript(intptr_t i, const char **outName,
                      void (**outFreeFunc)(const char *)) { ... }

    #if SWIFT_OBJC_INTEROP
      id quickLookObject() { ... }
    #endif
}

StructImplClassImpl 的實(shí)現(xiàn)主要有兩個(gè)不同:

第一, ClassImpl 實(shí)現(xiàn)了 quickLookObject 這個(gè)字段, 如果父類(lèi)是 OC 中的類(lèi)的話, 在使用 quickLookObject 時(shí)會(huì)調(diào)起 OC 的 debugQuickLookObject 方法.

#if SWIFT_OBJC_INTEROP
  id quickLookObject() {
    id object = [*reinterpret_cast<const id *>(value) retain];
    if ([object respondsToSelector:@selector(debugQuickLookObject)]) {
      id quickLookObject = [object debugQuickLookObject];
      [quickLookObject retain];
      [object release];
      return quickLookObject;
    }

    return object;
  }
#endif

第二, 如果該類(lèi)的父類(lèi)是 OC 的類(lèi),字段的偏移值需要在 OC 運(yùn)行時(shí)獲得.

uintptr_t fieldOffset;
if (usesNativeSwiftReferenceCounting(Clas)) {
  fieldOffset = Clas->getFieldOffsets()[i];
} else {
#if SWIFT_OBJC_INTEROP
  Ivar *ivars = class_copyIvarList((Class)Clas, nullptr);
  fieldOffset = ivar_getOffset(ivars[i]);
  free(ivars);
#else
  swift::crash("Object appears to be Objective-C, but no runtime.");
#endif

參考

how-mirror-works

[HandyJSON] 設(shè)計(jì)思路簡(jiǎn)析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咬像,一起剝皮案震驚了整個(gè)濱河市算撮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌县昂,老刑警劉巖肮柜,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異倒彰,居然都是意外死亡审洞,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)待讳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)芒澜,“玉大人,你說(shuō)我怎么就攤上這事创淡〕栈蓿” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵琳彩,是天一觀的道長(zhǎng)阅酪。 經(jīng)常有香客問(wèn)我,道長(zhǎng)汁针,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任砚尽,我火速辦了婚禮施无,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘必孤。我一直安慰自己猾骡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布敷搪。 她就那樣靜靜地躺著兴想,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赡勘。 梳的紋絲不亂的頭發(fā)上嫂便,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音闸与,去河邊找鬼毙替。 笑死岸售,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的厂画。 我是一名探鬼主播凸丸,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼袱院!你這毒婦竟也來(lái)了屎慢?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤忽洛,失蹤者是張志新(化名)和其女友劉穎腻惠,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體脐瑰,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妖枚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苍在。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绝页。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖寂恬,靈堂內(nèi)的尸體忽然破棺而出续誉,到底是詐尸還是另有隱情,我是刑警寧澤初肉,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布酷鸦,位于F島的核電站,受9級(jí)特大地震影響牙咏,放射性物質(zhì)發(fā)生泄漏臼隔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一妄壶、第九天 我趴在偏房一處隱蔽的房頂上張望摔握。 院中可真熱鬧,春花似錦丁寄、人聲如沸氨淌。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)盛正。三九已至,卻和暖如春屑埋,著一層夾襖步出監(jiān)牢的瞬間豪筝,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留壤蚜,地道東北人即寡。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像袜刷,于是被迫代替她去往敵國(guó)和親聪富。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學(xué)習(xí)記錄文檔著蟹,今天18年5月份再次想寫(xiě)文章墩蔓,發(fā)現(xiàn)簡(jiǎn)書(shū)還為我保存起的...
    Jenaral閱讀 2,768評(píng)論 2 9
  • 二零一七年七月三十日農(nóng)歷 六月初八,前往赤峰巴林右旗大板鎮(zhèn)萧豆。不知道那里得工作怎么樣奸披,不知道那里的環(huán)境怎么樣,不知道...
    青松玥冷閱讀 256評(píng)論 0 0
  • (寫(xiě)作于2009年9月15日) 如果說(shuō)人生是一部書(shū)泻拦,那么它的序言是那聲響亮的初啼才沧,后記是那段哀樂(lè)聲中的悼詞...
    萬(wàn)月生閱讀 217評(píng)論 1 3
  • 最好的朋友,也是我們公認(rèn)的美食西施小旭旭終于來(lái)通遼了暴构,最開(kāi)心的事兒不過(guò)如此啦览爵!么么噠^3^心里暖暖的置鼻! 講真,皮皮...
    萱萱_255b閱讀 253評(píng)論 0 0