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

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

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

HandyJSON 的優(yōu)勢

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

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

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

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

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

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

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

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

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

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

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

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

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

Mirror 的原理

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

class LCPerson {
    var name: String?
}

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

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

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

Swift 源碼

反射的 API 有一部分是用 Swift 實現(xiàn)的, 另一部分是 C++ 實現(xiàn)的. 分別在 ReflectionMirror.swiftReflectionMirror.mm. 這兩者通過一小組暴露給 Swift 的 C++ 函數(shù)進行通信.
比如在 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 中的實現(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ù)用于獲取對應(yīng)索引值的子元素信息.
  • @_silgen_name 修飾符會通知 Swift 編譯器將這個函數(shù)映射成 swift_reflectionMirror_subscript 符號,而不是 Swift 通常對應(yīng)到的 _getChild 方法名修飾. 也就是說, 在 Swift 中直接調(diào)用 _getChild 函數(shù), 實際上就是調(diào)用 C++ 實現(xiàn)的 swift_reflectionMirror_subscript 函數(shù).
  • SWIFT_CC(swift) 會告訴編譯器這個函數(shù)使用的是 Swift 的調(diào)用約定,而不是 C/C++ 的. SWIFT_RUNTIME_STDLIB_INTERFACE 標記這是個函數(shù).
  • C++ 的參數(shù)會去特意匹配在 Swift 中聲明的函數(shù)調(diào)用. 當(dāng) Swift 調(diào)用 _getChild 時, C++ 會用包含的 Swift 值指針的 value, 包含類型參數(shù)的 type, 包含目標索引值的 index, 包含標簽信息的 outname, 包含釋放目標字符串內(nèi)存的方法 outFreeFunc, 包含類型相應(yīng)的范型 <T> 的 T 的函數(shù)參數(shù)來調(diào)用此函數(shù).

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

獲取標準化類型
@_silgen_name("swift_reflectionMirror_normalizedType")
internal func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type

獲取子元素數(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)
獲取對象的展示類型
@_silgen_name("swift_reflectionMirror_displayStyle")
internal func _getDisplayStyle<T>(_: T) -> CChar

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

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

動態(tài)派發(fā)

元組、結(jié)構(gòu)、類和枚舉都需要不同的代碼去完成這些繁多的任務(wù),比如說查找子元素的數(shù)量, 比如針對 OC, Swift 做不同的處理. 所有的函數(shù)因為需要不同的類型的檢查而需要派發(fā)不同的實現(xiàn)代碼.

為了解決這個問題, Swift采用了一種類似動態(tài)派發(fā)的方式, 利用一個單獨的函數(shù)將 Swift 類型映射成一個 C++ 類的實例. 在一個實例上調(diào)用方法然后派發(fā)合適的實現(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 是實際需要傳入的Swift的值的指針.
  • T 是該值的靜態(tài)類型. 對應(yīng) Swift 中的范型參數(shù) <T>.
  • passedType 是被顯式傳遞進 Swift 側(cè)并且會實際應(yīng)用在反射過程中的類型(這個類型和在使用 Mirror 作為父類的實例在實際運行時的對象類型不一樣).
  • f 參數(shù)會傳遞這個函數(shù)查找到的會被調(diào)用的實現(xiàn)的對象引用.

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

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

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

以及通過 Switch 處理各種不同類型的實現(xiàn).

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

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

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

// Abstract base class for reflection implementations.
struct ReflectionMirrorImpl {
  const Metadata *type;  類型信息
  OpaqueValue *value;    值指針
  
  virtual char displayStyle() = 0;  顯示方式
  virtual intptr_t count() = 0;     子元素數(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ù)就會用 call 去調(diào)用相應(yīng)的方法.
比如前面提到的: swift_reflectionMirror_subscript, 獲取子元素信息. 內(nèi)部就會調(diào)用

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

大概有以下 5 種:


Tuple, Struct, Enum 等類型都有對應(yīng)的 ReflectionMirrorImpl 的實現(xiàn).

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

元組的反射

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

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

接下來從上往下看:

char displayStyle() {
    return 't';
}

返回 't' 的顯示樣式來表明這是一個元組.

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

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

using TupleTypeMetadata = TargetTupleTypeMetadata<InProcess>;

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

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

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

  • 對于元組而言, 這個名字是該元素在元組中的label, 若沒有l(wèi)abel, 默認就是一個 .0 的數(shù)值指示器.
  • 對于結(jié)構(gòu)體或者類來說, 這個名字是屬性名.
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 個位置的 label. label 是以空格為間隔存儲在 Tuple 中的 labels 字段里.

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

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

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

strdup() 在內(nèi)部調(diào)用了 malloc() 為變量分配內(nèi)存, 不需要使用返回的字符串時, 需要用 free() 釋放相應(yīng)的內(nèi)存空間, 否則會造成內(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 是一個 Element 類型的結(jié)構(gòu)體實例.
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 包含了一個 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 還包含了元素的類型, 通過類型和值的指針,去構(gòu)造一個包括這個值新的 Any 對象.
return AnyReturn(result);
  1. 通過 AnyReturn 包裝, 返回子元素信息. AnyReturn 是一個 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)體, 類, 枚舉的反射時, 先來看看一個函數(shù) swift_getFieldAt, 這個函數(shù)可以通過用語言的元數(shù)據(jù)去查找類型信息. HandyJSON 里面直接用到了.

swift_getFieldAt

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

函數(shù)原型:

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

接下來從上往下看.

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

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

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

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

接下來的工作分為兩步.

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

獲取符號還原器, 將符號修飾過的類名還原為實際的類型引用.

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;
};

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

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

如果存在字段描述符緩存, 那么通過 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;
    }
  }

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

接下來看看 getFieldAt 內(nèi)部的實現(xiàn)過程.
getFieldAt 這個方法作用是將字段描述符轉(zhuǎ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. 判斷是否有類型. 比如, 字段實際上是一個枚舉, 那么它可能沒有類型.
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. 定義一個 ContextDescriptor 類型的指針 descriptorPath, 通過 baseSesc 獲取描述符的路徑. 也就是將這個類型的所有范型的上下文抽離出來.
auto typeInfo = _getTypeByMangledName(
        field.getMangledTypeName(0),
        [&](unsigned depth, unsigned index) -> const Metadata * { ... }
  1. 獲取類型信息

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

_getTypeByMangledName 方法的作用在此.

_getTypeByMangledName 內(nèi)部,

field.getMangledTypeName(0)

首先傳入由符號修飾的類型.

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

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

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;
}

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

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

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

return base->getGenericArgs()[flatIndex];

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

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

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

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

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

struct StructImpl : ReflectionMirrorImpl {
    顯示方式, 結(jié)構(gòu)體是 's'
    char displayStyle() { ... }
    
    子元素數(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 由原來的 MetaType 類型的指針轉(zhuǎn)為 StructMetadata 類型的指針.

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

注意: StructMetadata 類型實際是 TargetStructMetadata 類型, 這是一個 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 類型的指針.
if (i < 0 || (size_t)i > Struct->getDescription()->NumFields)
swift::crash("Swift mirror subscript bounds check failure");
  1. 進行邊界檢查, 防止訪問不存在的子元素.
auto fieldOffset = Struct->getFieldOffsets()[i];
  1. 查找對應(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;
  
  計算字段儲存的指針
  auto *bytes = reinterpret_cast<char*>(value);
  auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);
  
  loadSpecialReferenceStorage 方法用于處理將字段的值復(fù)制到 Any 返回值以處理弱引用
  bool didLoad = loadSpecialReferenceStorage(fieldData, fieldInfo, &result);
  
  如果值沒有被載入的話那么那個值用普通的儲存,并且以普通的方式拷貝到返回值
  if (!didLoad) {
    result.Type = fieldInfo.getType();
    auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer);
    result.Type->vw_initializeWithCopy(opaqueValueAddr,
                                       const_cast<OpaqueValue *>(fieldData));
  }
});
  1. 通過 _swift_getFieldAt 方法, 獲取結(jié)構(gòu)體字段中的信息(字段的名字和類型).
AnyReturn(result);

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

類的反射

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

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

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

第一, ClassImpl 實現(xiàn)了 quickLookObject 這個字段, 如果父類是 OC 中的類的話, 在使用 quickLookObject 時會調(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

第二, 如果該類的父類是 OC 的類,字段的偏移值需要在 OC 運行時獲得.

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è)計思路簡析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容