Swift中的反射Mirror

Swift中的反射Mirror

[TOC]

前言

MirrorSwift中的反射機制,對于C#Java開發(fā)人員來說秕噪,應(yīng)該很熟悉反射這個概念拘荡。反射就是可以動態(tài)的獲取類型以及成員信息胳徽,同時也可以在運行時動態(tài)的調(diào)用方法和屬性等伍派。

對于iOS開發(fā)人員來說鸭你,入門時使用的Objective-C是很少強調(diào)反射概念的屈张,因為OCRuntime要比其他語言的反射強大的多。

另外袱巨,在閱讀本篇文章前建議先看看我的另一篇文章:
Swift 中的類型

1. Mirror 簡介

MirrorSwift中的反射機制的實現(xiàn)阁谆,它的本質(zhì)是一個結(jié)構(gòu)體。其部分源碼(Swift 5.3.1)如下:

public struct Mirror {

  /// A suggestion of how a mirror's subject is to be interpreted.
  ///
  /// Playgrounds and the debugger will show a representation similar
  /// to the one used for instances of the kind indicated by the
  /// `DisplayStyle` case name when the mirror is used for display.
  public enum DisplayStyle {
    case `struct`, `class`, `enum`, tuple, optional, collection
    case dictionary, `set`
  }
    
    /// The static type of the subject being reflected.
    ///
    /// This type may differ from the subject's dynamic type when this mirror
    /// is the `superclassMirror` of another mirror.
    public let subjectType: Any.Type

    /// A collection of `Child` elements describing the structure of the
    /// reflected subject.
    public let children: Children

    /// A suggested display style for the reflected subject.
    public let displayStyle: DisplayStyle?

    /// A mirror of the subject's superclass, if one exists.
    public var superclassMirror: Mirror? {
      return _makeSuperclassMirror()
    }
}
  • subjectType:表示類型愉老,被反射主體的類型
  • children:子元素集合
  • displayStyle:顯示類型场绿,基本類型為nil 枚舉值: struct, class, enum, tuple, optional, collection, dictionary, set
  • superclassMirror:父類反射, 沒有父類為nil

除了這些屬性還有一些初始化方法嫉入,我們最常用的就是初始化方法就是:

  /// Creates a mirror that reflects on the given instance.
  ///
  /// If the dynamic type of `subject` conforms to `CustomReflectable`, the
  /// resulting mirror is determined by its `customMirror` property.
  /// Otherwise, the result is generated by the language.
  ///
  /// If the dynamic type of `subject` has value semantics, subsequent
  /// mutations of `subject` will not observable in `Mirror`.  In general,
  /// though, the observability of mutations is unspecified.
  ///
  /// - Parameter subject: The instance for which to create a mirror.
  public init(reflecting subject: Any) {
    if case let customized as CustomReflectable = subject {
      self = customized.customMirror
    } else {
      self = Mirror(internalReflecting: subject)
    }
  }

根據(jù)源碼我們還可以看到能夠使用customMirror焰盗,這個沒太研究,在源碼中可以看到很多CustomMirror的身影劝贸,感興趣的可以去研究研究姨谷。對于非customMirror的統(tǒng)一使用Mirror(internalReflecting:)進行初始化。

關(guān)于customMirror的補充映九,摘抄自swiftGG Mirror 的工作原理梦湘。

Mirror 允許類型用遵循 CustomReflectable 協(xié)議的方式提供一個自定義的表示方式。這給那些想表示得比內(nèi)建形式更友好的類型提供一種有效的方法件甥。 比如 Array 類型遵守 CustomReflectable 協(xié)議并且暴露其中的元素為無標簽的 Children捌议。Dictionary 使用這種方法暴露其中的鍵值對為帶標簽的 Children

2. Mirror的簡單使用

2.1 基本使用

這里我們通過使用Mirror打印對象的屬性名稱和屬性值引有。

class Person {
    var name: String = "xiaohei"
    var age: Int = 18
    var height = 1.85
}

var p = Person()
var mirror = Mirror(reflecting: p.self)

print("對象類型:\(mirror.subjectType)")
print("對象屬性個數(shù):\(mirror.children.count)")
print("對象的屬性及屬性值")
for child in mirror.children {
    print("\(child.label!)---\(child.value)")
}

打印結(jié)果:

image

我們可以看到瓣颅,屬性名稱和值都已經(jīng)正常打印。

2.2 將對象轉(zhuǎn)換為字典

首先我們來體驗一下將對象轉(zhuǎn)換為字典譬正。

class Animal {
    var name: String?
    var color: String?
    private var birthday: Date = Date(timeIntervalSince1970: 0)
}
 
class Cat: Animal {
    var master = "小黑"
    var like: [String] = ["mouse", "fish"]
    
    override init() {
        super.init()
        color = "黃色"
    }
}

func mapDic(mirror: Mirror) -> [String: Any] {
    var dic: [String: Any] = [:]
    for child in mirror.children {
        // 如果沒有l(wèi)abe就會被拋棄
        if let label = child.label {
            let propertyMirror = Mirror(reflecting: child.value)
            print(propertyMirror)
            dic[label] = child.value
        }
    }
    // 添加父類屬性
    if let superMirror = mirror.superclassMirror {
        let superDic = mapDic(mirror: superMirror)
        for p in superDic {
            dic[p.key] = p.value
        }
    }
    return dic
}


// Mirror使用
let cat = Cat()
cat.name = "大橘為重"
let mirror = Mirror(reflecting: cat)
let mirrorDic = mapDic(mirror: mirror)
print(mirrorDic)

打印結(jié)果:

image

通過打印結(jié)果我們可以看到宫补,對于一些基本類型,已經(jīng)可選類型的數(shù)據(jù)都已經(jīng)轉(zhuǎn)換為字典值曾我,對于私有屬性也可以完成轉(zhuǎn)換粉怕。如果里面包含類則還需要進行遞歸處理。

2.3 轉(zhuǎn) JSON

注: 這里并沒有真正的轉(zhuǎn)換成json字符串抒巢,還是只轉(zhuǎn)換成了字典贫贝,重要在思想,如果需要轉(zhuǎn)換成json還需要很多優(yōu)化,以及特殊字符串的考量稚晚。

其實提到反射我們想到最多的應(yīng)該就是JSON了崇堵,這里我們利用Mirror的特性,將對象轉(zhuǎn)換成字典客燕,對基本類型和類做了相應(yīng)的處理鸳劳,體會一下轉(zhuǎn)json的思路。

首先我們定義一個Person對象幸逆,代碼如下:

struct Person {
    var name: String = "xiaohei"
    var age: Int = 18
    var isMale: Bool = true
    var address: Address? = Address(street: "xizhimen North")
    var height = 1.85
    var like: Array = ["eat", "sleep", "play"]
    var weight: Float = 75.0
    var some: Int?
}

struct Address {
    var street: String
}

// 創(chuàng)建一個Person對象
let p = Person()

為了通用性棍辕,我們可以編寫一個協(xié)議暮现,用來為所有類型提供轉(zhuǎn)換的方法慕爬,只需要遵守該協(xié)議就可以使用協(xié)議中的方法寞冯。

//可以轉(zhuǎn)換為 Json 的協(xié)議
protocol CustomJSONProtocol {
    func toJSON() throws -> Any?
}

協(xié)議的實現(xiàn)過程中會有些錯誤,我們也簡單的定義個枚舉,方便處理逢倍。為了更加詳細的描述錯誤信息,我們添加了錯誤描述和錯誤code骤竹。

// 轉(zhuǎn) json 時的錯誤類型
enum JSONMapError: Error{
    case emptyKey
    case notConformProtocol
}

// 錯誤描述
extension JSONMapError: LocalizedError{
    var errorDescription: String?{
        switch self {
        case .emptyKey:
            return "key 為空"
        case .notConformProtocol:
           return "沒遵守協(xié)議"
        }
    }
}

// errorcode
extension JSONMapError: CustomNSError{
    var errorCode: Int{
        switch self {
        case .emptyKey:
            return 100
        case .notConformProtocol:
            return 101
        }
    }
}

協(xié)議實現(xiàn)的代碼:

extension CustomJSONProtocol {
    func toJSON() throws -> Any? {
        
        //創(chuàng)建 Mirror 類型
        let mirror = Mirror(reflecting: self)
        // 如果沒有屬性乒验,比如一般類型String、Int等电媳,直接返回自己
        guard !mirror.children.isEmpty else { return self }
        
        var result: [String:Any] = [:]
        // 遍歷
        for children in mirror.children {
            if let value = children.value as? CustomJSONProtocol{
                if let key = children.label {
                    print(key)
                    result[key] = try value.toJSON()
                } else {
                   throw JSONMapError.emptyKey
                }
            } else {
                  throw JSONMapError.notConformProtocol
            }
        }
        
        return result
    }
}

將用到的類型都遵守協(xié)議

//將一般類型都遵從 CustomJSONProtocol 協(xié)議
extension Person: CustomJSONProtocol {}
extension String: CustomJSONProtocol {}
extension Int: CustomJSONProtocol {}
extension Bool: CustomJSONProtocol {}
extension Double: CustomJSONProtocol {}
extension Float: CustomJSONProtocol {}
    
extension Address: CustomJSONProtocol {}

// 數(shù)組需要單獨處理踏揣,要不然就會報錯emptyKey
extension Array: CustomJSONProtocol {
    func toJSON() throws -> Any? {
        return self
    }
}

//Optionai 需要特別對待,原因是如果直接返回匾乓,則會是 .Some: [...]
extension Optional: CustomJSONProtocol {
    func toJSON() throws -> Any? {
        if let x = self {
            if let value = x as? CustomJSONProtocol {
                return try value.toJSON()
            }
            throw JSONMapError.notConformProtocol
        }
        return nil
    }
}

最后我們打印一下:

do {
    print(try p.toJSON()!)
} catch {
    print(error.localizedDescription)
    print((error as? JSONMapError)?.errorCode)
}

打印結(jié)果:

image

我們看到捞稿,對于some這空值,并沒有存儲到字典中拼缝,因為swift中的字典對于空值是刪除的意思娱局。

如果想將其轉(zhuǎn)換成json還需修改"[]"為"{}",這個對于數(shù)組和對象還不好區(qū)分咧七,另外對于json字符串內(nèi)的一些value也有可能是應(yīng)一串json還需要添加轉(zhuǎn)義字符等衰齐。

所以總的來說,思路是這樣的继阻,要想真正的做成通用的轉(zhuǎn)json的方案還需要很多的優(yōu)化耻涛,比如說,我們不可能將所有的基本類型都去遵守一個協(xié)議瘟檩,這時候我們也可以考慮使用泛型去作為方法的參數(shù)抹缕。

3. Mirror 源碼解析

源碼版本Swift 5.3.1

在本章節(jié)我們將分析Mirror的部分源碼,查看其底層實現(xiàn)芒帕,最后通過Swift代碼使用內(nèi)存重綁定的形式歉嗓,仿寫一下Mirror,來更好的探索Mirror的原理背蟆,理解Mirror的思想鉴分。

我們知道Swift是一門靜態(tài)語言哮幢,那么在底層是如何實現(xiàn)的獲取對應(yīng)的屬性值的呢?又或者說Swift的反射特性是如何實現(xiàn)的呢志珍?下面我們通過對Mirror底層源碼的探索來尋找答案橙垢。

3.1 代碼結(jié)構(gòu)

Mirror的實現(xiàn)是由一部分Swift代碼加上另一部分C++代碼。Swift代碼實現(xiàn)在ReflectionMirror.swift文件中伦糯,C++代碼實現(xiàn)在ReflectionMirror.mm文件中柜某。Swift更適合用在實現(xiàn)更Swift的接口,但是在Swift中不能直接訪問C++的類敛纲。這里使用了@_silgen_name來實現(xiàn)Swift調(diào)用C++中的方法喂击。舉個例子:

@_silgen_name("swift_reflectionMirror_count")
internal func _getChildCount<T>(_: T, type: Any.Type) -> Int

@_silgen_name修飾符會通知Swift編譯器將這個函數(shù)映射成swift_reflectionMirror_count符號,而不是Swift通常對應(yīng)到的_getChildCount方法名修飾淤翔。需要注意的是翰绊,最前面的下劃線表示這個修飾是被保留在標準庫中的。在C++這邊旁壮,這個函數(shù)是這樣的监嗜。

// func _getChildCount<T>(_: T, type: Any.Type) -> Int
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
intptr_t swift_reflectionMirror_count(OpaqueValue *value,
                                      const Metadata *type,
                                      const Metadata *T) {
  return call(value, T, type, [](ReflectionMirrorImpl *impl) {
    return impl->count();
  });
}

SWIFT_CC(swift)會告訴編譯器這個函數(shù)使用的是Swift的調(diào)用約定,而不是C/C++的抡谐,SWIFT_RUNTIME_STDLIB_API標記這個函數(shù)裁奇,在Swift側(cè)的一部分接口中,而且它還有標記為extern "C"的作用麦撵,從而避免C++的方法名修飾刽肠,并確保它在Swift側(cè)會有預(yù)期的符號。同時C++的參數(shù)會去特意匹配在Swift中聲明的函數(shù)調(diào)用厦坛。當Swift調(diào)用_getChildCount時五垮,C++會用包含Swift值指針的value,包含類型參數(shù)type杜秸,包含類型響應(yīng)的泛型<T>T的函數(shù)參數(shù)來調(diào)用此函數(shù)放仗。

簡單的說就是使用@_silgen_name("xxx")修飾符修飾的Swift方法會調(diào)用括號中的xxx的符號,不管是C++的還是C的都可以撬碟。

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

@_silgen_name("swift_isClassType")
internal func _isClassType(_: Any.Type) -> Bool

@_silgen_name("swift_getMetadataKind")
internal func _metadataKind(_: Any.Type) -> UInt

@_silgen_name("swift_reflectionMirror_normalizedType")
internal func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type

@_silgen_name("swift_reflectionMirror_count")
internal func _getChildCount<T>(_: T, type: Any.Type) -> Int

@_silgen_name("swift_reflectionMirror_recursiveCount")
internal func _getRecursiveChildCount(_: Any.Type) -> Int

@_silgen_name("swift_reflectionMirror_recursiveChildMetadata")
internal func _getChildMetadata(
  _: Any.Type,
  index: Int,
  outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
  outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
) -> Any.Type

@_silgen_name("swift_reflectionMirror_recursiveChildOffset")
internal func _getChildOffset(
  _: Any.Type,
  index: Int
) -> Int

internal typealias NameFreeFunc = @convention(c) (UnsafePointer<CChar>?) -> Void

@_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

internal func getChild<T>(of value: T, type: Any.Type, index: Int) -> (label: String?, value: Any) {
  var nameC: UnsafePointer<CChar>? = nil
  var freeFunc: NameFreeFunc? = nil
  
  let value = _getChild(of: value, type: type, index: index, outName: &nameC, outFreeFunc: &freeFunc)
  
  let name = nameC.flatMap({ String(validatingUTF8: $0) })
  freeFunc?(nameC)
  return (name, value)
}

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

@_silgen_name("_swift_stdlib_NSObject_isKindOfClass")
internal func _isImpl(_ object: AnyObject, kindOf: UnsafePointer<CChar>) -> Bool

3.2 初始化

在一開始我們簡單的介紹了Mirror的部分源碼诞挨,也由此知道Mirror(reflecting:)初始化方法可以接受任意值,返回一個提供該值子元素集合Children的相關(guān)信息的實例呢蛤。

通過Mirror(reflecting:)源碼我們可以知道惶傻,其底層調(diào)用的是internalReflecting方法。在ReflectionMirror.swift文件的extension Mirror中我們可以找到該方法其障。其源碼如下:

3.2.1 internalReflecting

 internal init(internalReflecting subject: Any,
              subjectType: Any.Type? = nil,
              customAncestor: Mirror? = nil)
  {
    let subjectType = subjectType ?? _getNormalizedType(subject, type: type(of: subject))
    
    let childCount = _getChildCount(subject, type: subjectType)
    let children = (0 ..< childCount).lazy.map({
      getChild(of: subject, type: subjectType, index: $0)
    })
    self.children = Children(children)
    
    self._makeSuperclassMirror = {
      guard let subjectClass = subjectType as? AnyClass,
            let superclass = _getSuperclass(subjectClass) else {
        return nil
      }
      
      // Handle custom ancestors. If we've hit the custom ancestor's subject type,
      // or descendants are suppressed, return it. Otherwise continue reflecting.
      if let customAncestor = customAncestor {
        if superclass == customAncestor.subjectType {
          return customAncestor
        }
        if customAncestor._defaultDescendantRepresentation == .suppressed {
          return customAncestor
        }
      }
      return Mirror(internalReflecting: subject,
                    subjectType: superclass,
                    customAncestor: customAncestor)
    }
    
    let rawDisplayStyle = _getDisplayStyle(subject)
    switch UnicodeScalar(Int(rawDisplayStyle)) {
    case "c": self.displayStyle = .class
    case "e": self.displayStyle = .enum
    case "s": self.displayStyle = .struct
    case "t": self.displayStyle = .tuple
    case "\0": self.displayStyle = nil
    default: preconditionFailure("Unknown raw display style '\(rawDisplayStyle)'")
    }
    
    self.subjectType = subjectType
    self._defaultDescendantRepresentation = .generated
  }

源碼分析:

  • 首先是獲取subjectType银室,如果傳入的有值就使用傳入的值,否則就通過_getNormalizedType函數(shù)去獲取
  • 接下來就是通過_getChildCount獲取childCount
  • 接下來是children,注意這里是懶加載的
  • 緊接著是SuperclassMirror蜈敢,這里使用的是一個閉包的形式
  • 最后會獲取并解析顯示的樣式辜荠,并設(shè)置Mirror剩下的屬性。

3.2.2 _getNormalizedType

下面我們就來看看_getNormalizedType函數(shù)內(nèi)部的實現(xiàn)抓狭。根據(jù)上面的分析我們知道實際調(diào)用是swift_reflectionMirror_normalizedType伯病。在ReflectionMirror.mm文件中我們可以看到其源碼:

// func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
const Metadata *swift_reflectionMirror_normalizedType(OpaqueValue *value,
                                                      const Metadata *type,
                                                      const Metadata *T) {
  return call(value, T, type, [](ReflectionMirrorImpl *impl) { return impl->type; });
}

3.2.3 call函數(shù)

我們可以看到這里調(diào)用了一個call函數(shù),最后返回的是impltype否过。首先我們看看這call函數(shù):

template<typename F>
auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedType,
          const F &f) -> decltype(f(nullptr))
{
  const Metadata *type;
  OpaqueValue *value;
  std::tie(type, value) = unwrapExistential(T, passedValue);
  
  if (passedType != nullptr) {
    type = passedType;
  }
  
  auto call = [&](ReflectionMirrorImpl *impl) {
    impl->type = type;
    impl->value = value;
    auto result = f(impl);
    return result;
  };
  
  auto callClass = [&] {
    if (passedType == nullptr) {
      // Get the runtime type of the object.
      const void *obj = *reinterpret_cast<const void * const *>(value);
      auto isa = _swift_getClass(obj);

      // Look through artificial subclasses.
      while (isa->isTypeMetadata() && isa->isArtificialSubclass()) {
        isa = isa->Superclass;
      }
      passedType = isa;
    }

  #if SWIFT_OBJC_INTEROP
    // If this is a pure ObjC class, reflect it using ObjC's runtime facilities.
    // ForeignClass (e.g. CF classes) manifests as a NULL class object.
    auto *classObject = passedType->getClassObject();
    if (classObject == nullptr || !classObject->isTypeMetadata()) {
      ObjCClassImpl impl;
      return call(&impl);
    }
  #endif

    // Otherwise, use the native Swift facilities.
    ClassImpl impl;
    return call(&impl);
  };
  
  switch (type->getKind()) {
    case MetadataKind::Tuple: {
      TupleImpl impl;
      return call(&impl);
    }

    case MetadataKind::Struct: {
      StructImpl impl;
      return call(&impl);
    }
    

    case MetadataKind::Enum:
    case MetadataKind::Optional: {
      EnumImpl impl;
      return call(&impl);
    }
      
    case MetadataKind::ObjCClassWrapper:
    case MetadataKind::ForeignClass:
    case MetadataKind::Class: {
      return callClass();
    }

    case MetadataKind::Metatype:
    case MetadataKind::ExistentialMetatype: {
      MetatypeImpl impl;
      return call(&impl);
    }

    case MetadataKind::Opaque: {
#if SWIFT_OBJC_INTEROP
      // If this is the AnyObject type, use the dynamic type of the
      // object reference.
      if (type == &METADATA_SYM(BO).base) {
        return callClass();
      }
#endif
      // If this is the Builtin.NativeObject type, and the heap object is a
      // class instance, use the dynamic type of the object reference.
      if (type == &METADATA_SYM(Bo).base) {
        const HeapObject *obj
          = *reinterpret_cast<const HeapObject * const*>(value);
        if (obj->metadata->getKind() == MetadataKind::Class) {
          return callClass();
        }
      }
      LLVM_FALLTHROUGH;
    }

    /// TODO: Implement specialized mirror witnesses for all kinds.
    default:
      break;

    // Types can't have these kinds.
    case MetadataKind::HeapLocalVariable:
    case MetadataKind::HeapGenericLocalVariable:
    case MetadataKind::ErrorObject:
      swift::crash("Swift mirror lookup failure");
    }

    // If we have an unknown kind of type, or a type without special handling,
    // treat it as opaque.
    OpaqueImpl impl;
    return call(&impl);
}

乍一看這個call函數(shù)代碼相對有點多午笛,其實主要就是一個大型的switch聲明,和一些對特殊情況的處理苗桂,這里重要的就是药磺,它會用一個ReflectionMirrorImpl的子類實例去結(jié)束調(diào)用f,然后會調(diào)用這個實例上的方法去讓真正的工作完成誉察,這也就是為什么在swift_reflectionMirror_normalizedType函數(shù)中最后會return impl->type;感興趣的可以通過源碼去調(diào)試一下与涡,這里我也調(diào)試了,沒什么指的說的持偏,這就就不寫調(diào)試過程了,下面我們就來看看ReflectionMirrorImpl氨肌。

3.2.4 ReflectionMirrorImpl

ReflectionMirrorImpl有以下6個子類:

  • TupleImpl 元組的反射
  • StructImpl 結(jié)構(gòu)體的反射
  • EnumImpl 枚舉的反射
  • ClassImpl 類的反射
  • MetatypeImpl 元數(shù)據(jù)的反射
  • OpaqueImpl 不透明類型的反射

ReflectionMirrorImpl 源碼:

// Abstract base class for reflection implementations.
struct ReflectionMirrorImpl {
  const Metadata *type;
  OpaqueValue *value;
  
  virtual char displayStyle() = 0;
  virtual intptr_t count() = 0;
  virtual intptr_t childOffset(intptr_t index) = 0;
  virtual const FieldType childMetadata(intptr_t index,
                                        const char **outName,
                                        void (**outFreeFunc)(const char *)) = 0;
  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
  
  // For class types, traverse through superclasses when providing field
  // information. The base implementations call through to their local-only
  // counterparts.
  virtual intptr_t recursiveCount() {
    return count();
  }
  virtual intptr_t recursiveChildOffset(intptr_t index) {
    return childOffset(index);
  }
  virtual const FieldType recursiveChildMetadata(intptr_t index,
                                                 const char **outName,
                                                 void (**outFreeFunc)(const char *))
  {
    return childMetadata(index, outName, outFreeFunc);
  }

  virtual ~ReflectionMirrorImpl() {}
};

ReflectionMirrorImpl源碼不多鸿秆,但是我們可以看到type以及count等都在其中。下面我們以結(jié)構(gòu)體為例怎囚,看看其子類的源碼卿叽。

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

// Implementation for structs.
struct StructImpl : ReflectionMirrorImpl {
  bool isReflectable() {
    const auto *Struct = static_cast<const StructMetadata *>(type);
    const auto &Description = Struct->getDescription();
    return Description->isReflectable();
  }

  char displayStyle() {
    return 's';
  }
  
  intptr_t count() {
    if (!isReflectable()) {
      return 0;
    }

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

  intptr_t childOffset(intptr_t i) {
    auto *Struct = static_cast<const StructMetadata *>(type);

    if (i < 0 || (size_t)i > Struct->getDescription()->NumFields)
      swift::crash("Swift mirror subscript bounds check failure");

    // Load the offset from its respective vector.
    return Struct->getFieldOffsets()[i];
  }

  const FieldType childMetadata(intptr_t i, const char **outName,
                                void (**outFreeFunc)(const char *)) {
    StringRef name;
    FieldType fieldInfo;
    std::tie(name, fieldInfo) = getFieldAt(type, i);
    assert(!fieldInfo.isIndirect() && "indirect struct fields not implemented");
    
    *outName = name.data();
    *outFreeFunc = nullptr;
    
    return fieldInfo;
  }

  AnyReturn subscript(intptr_t i, const char **outName,
                      void (**outFreeFunc)(const char *)) {
    auto fieldInfo = childMetadata(i, outName, outFreeFunc);

    auto *bytes = reinterpret_cast<char*>(value);
    auto fieldOffset = childOffset(i);
    auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);

    return copyFieldContents(fieldData, fieldInfo);
  }
};
  • 首先一個判斷是否支持反射的方法,最中是訪問的Description->isReflectable()
  • 這里使用‘s’來顯式的表明這是一個結(jié)構(gòu)體
  • 然后是獲取屬性個數(shù)
  • 緊接著是獲取每個屬性的偏移值
  • 然后獲取了FieldType內(nèi)部還可以獲取到屬性的名稱恳守。
  • 最后subscript方法可以獲取到屬性名稱和屬性偏移的指針考婴,也就是屬性值。

3.3 Description

在此處我們看到很多關(guān)于Description的代碼催烘,看來這個Description存儲著很多信息沥阱,在獲取Description的時候是從StructMetadata通過getDescription()方法獲取到。所以這些信息基本確定是從MetaData中獲取到的伊群。StructMetadataTargetStructMetadata的別名考杉,我們以此為例:

3.3.1 TargetStructMetadata

TargetStructMetadata我們可以看到如下代碼:

const TargetStructDescriptor<Runtime> *getDescription() const {
    return llvm::cast<TargetStructDescriptor<Runtime>>(this->Description);
}

說明此處會返回一個TargetStructDescriptor類型的Description,但是在這里并沒有找到這個屬性舰始,可能在父類中崇棠,我們可以看到TargetStructMetadata繼承自TargetValueMetadata

3.3.2 TargetValueMetadata

在這里我們可以看到如下代碼:

/// An out-of-line description of the type.
TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;
  
getDescription() const {
    return Description;
}

這里我們便找到了:

  • Description屬性丸卷,它的類型是TargetValueTypeDescriptor枕稀,應(yīng)該是TargetStructDescriptor的父類。
  • getDescription()方法,在TargetStructMetadata是重寫的這個方法

3.3.3 TargetStructDescriptor

跳轉(zhuǎn)到TargetStructDescriptor中后萎坷,我們可以看到:

  • TargetValueTypeDescriptor果然是它的父類
  • 在其源碼中我們還可以發(fā)現(xiàn)兩個屬性范抓,分別是NumFieldsFieldOffsetVectorOffset源碼如下:
/// The number of stored properties in the struct.
  /// If there is a field offset vector, this is its length.
  uint32_t NumFields;
  /// The offset of the field offset vector for this struct's stored
  /// properties in its metadata, if any. 0 means there is no field offset
  /// vector.
  uint32_t FieldOffsetVectorOffset;
  • 其中NumFields主要表示結(jié)構(gòu)體中屬性的個數(shù),如果只有一個字段偏移量則表示偏移量的長度
  • FieldOffsetVectorOffset表示這個結(jié)構(gòu)體元數(shù)據(jù)中存儲的屬性的字段偏移向量的偏移量匕垫,如果是0則表示沒有

3.3.4 TargetValueTypeDescriptor

源碼如下象泵,很少:

template <typename Runtime>
class TargetValueTypeDescriptor
    : public TargetTypeContextDescriptor<Runtime> {
public:
  static bool classof(const TargetContextDescriptor<Runtime> *cd) {
    return cd->getKind() == ContextDescriptorKind::Struct ||
           cd->getKind() == ContextDescriptorKind::Enum;
  }
};

在這里我們并沒有找到太多有用的信息,我們繼續(xù)向父類尋找偶惠,其繼承自TargetTypeContextDescriptor

3.3.5 TargetTypeContextDescriptor

部分源碼:

template <typename Runtime>
class TargetTypeContextDescriptor
    : public TargetContextDescriptor<Runtime> {
public:
  /// The name of the type.
  TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name;

  /// A pointer to the metadata access function for this type.
  ///
  /// The function type here is a stand-in. You should use getAccessFunction()
  /// to wrap the function pointer in an accessor that uses the proper calling
  /// convention for a given number of arguments.
  TargetRelativeDirectPointer<Runtime, MetadataResponse(...),
                              /*Nullable*/ true> AccessFunctionPtr;
  
  /// A pointer to the field descriptor for the type, if any.
  TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
                              /*nullable*/ true> Fields;
}

我們可以看到:

  • 該類繼承自TargetContextDescriptor
  • Name朗涩、AccessFunctionPtr忽孽、Fields三個屬性
    • 其中name就是類型的名稱
    • AccessFunctionPtr是該類型元數(shù)據(jù)訪問函數(shù)的指針
    • Fields是一個指向該類型的字段描述符的指針

3.3.6 TargetContextDescriptor

接下來我們再看看TargetTypeContextDescriptor的父類中還有什么有用的信息兄一。部分源碼如下:

/// Base class for all context descriptors.
template<typename Runtime>
struct TargetContextDescriptor {
  /// Flags describing the context, including its kind and format version.
  ContextDescriptorFlags Flags;
  
  /// The parent context, or null if this is a top-level context.
  TargetRelativeContextPointer<Runtime> Parent;
}

這里我們可以看到:

  • 這就是descriptors的基類
  • 有兩個屬性出革,分別是FlagsParent
    • 其中Flags是描述上下文的標志骂束,包括它的種類和格式版本展箱。
    • Parent是記錄父類上下文的混驰,如果是頂級則為null

3.3.7 小結(jié)

至此我們對結(jié)構(gòu)體Description的層級結(jié)構(gòu)基本就理清楚了账胧,現(xiàn)總結(jié)如下:

image

從上圖我們可以看到,對于一個結(jié)構(gòu)體的Description來說遮精,繼承鏈上一共四個類准脂,7個屬性狸膏。下面我們就對這些屬性作進一步的分析

3.4 Description中的屬性

3.4.1 Flags

Flags的類型是ContextDescriptorFlags,我們點擊跳轉(zhuǎn)進去贤旷,我們可以看到:

/// Common flags stored in the first 32-bit word of any context descriptor.
struct ContextDescriptorFlags {
private:
  uint32_t Value;

  explicit constexpr ContextDescriptorFlags(uint32_t Value)
    : Value(Value) {}
public:
  constexpr ContextDescriptorFlags() : Value(0) {}
  constexpr ContextDescriptorFlags(ContextDescriptorKind kind,
                                   bool isGeneric,
                                   bool isUnique,
                                   uint8_t version,
                                   uint16_t kindSpecificFlags)
    : ContextDescriptorFlags(ContextDescriptorFlags()
                               .withKind(kind)
                               .withGeneric(isGeneric)
                               .withUnique(isUnique)
                               .withVersion(version)
                               .withKindSpecificFlags(kindSpecificFlags))
  {}
  
  ......
}

從以上的代碼中我們可以看到這個FLags實際是個uint32_t的值幼驶,按位存儲著kind盅藻、isGeneric氏淑、isUnique夸政、version等信息。

3.4.2 Parent

Parent的類型是TargetRelativeContextPointer<Runtime>坑资,我們看看TargetRelativeContextPointer袱贮,點擊跳轉(zhuǎn)過去:

using TargetRelativeContextPointer =
  RelativeIndirectablePointer<const Context<Runtime>,
                              /*nullable*/ true, int32_t,
                              TargetSignedContextPointer<Runtime, Context>>;

我們可以看到TargetRelativeContextPointer是取自RelativeIndirectablePointer的別名攒巍,繼續(xù)點擊進行查看:

/// A relative reference to an object stored in memory. The reference may be
/// direct or indirect, and uses the low bit of the (assumed at least
/// 2-byte-aligned) pointer to differentiate.
template<typename ValueTy, bool Nullable = false, typename Offset = int32_t, typename IndirectType = const ValueTy *>
class RelativeIndirectablePointer {

    /// The relative offset of the pointer's memory from the `this` pointer.
    /// If the low bit is clear, this is a direct reference; otherwise, it is
    /// an indirect reference.
    Offset RelativeOffsetPlusIndirect;
  
        const ValueTy *get() const & {
        static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
                      "alignment of value and offset must be at least 2 to "
                      "make room for indirectable flag");
      
        // Check for null.
        if (Nullable && RelativeOffsetPlusIndirect == 0)
          return nullptr;
        
        Offset offsetPlusIndirect = RelativeOffsetPlusIndirect;
        uintptr_t address = detail::applyRelativeOffset(this,
                                                        offsetPlusIndirect & ~1);
    
        // If the low bit is set, then this is an indirect address. Otherwise,
        // it's direct.
        if (offsetPlusIndirect & 1) {
          return *reinterpret_cast<IndirectType const *>(address);
        } else {
          return reinterpret_cast<const ValueTy *>(address);
        }   
    }
}

根據(jù)注釋我們就可以輕松的知道這個類的主要作用是存儲在內(nèi)存中的對象的相對引用。這個意思在后面也有相關(guān)的解釋就是在內(nèi)存中引用可以是直接的也可以是間接的兢孝,直接的就是存儲的絕對地址(也不一定雳殊,還有ASLR等)夯秃,直接訪問這個地址就可以拿到對應(yīng)的數(shù)據(jù)仓洼,而這里的相對引用就是間接的衬潦,這里面通過RelativeOffsetPlusIndirect屬性存儲相對的地址偏移量镀岛,在通過get()函數(shù)獲取,在get()函數(shù)中走越,會調(diào)用applyRelativeOffset函數(shù)旨指,進行地址的偏移谆构,applyRelativeOffset源碼:

/// Apply a relative offset to a base pointer. The offset is applied to the base
/// pointer using sign-extended, wrapping arithmetic.
template<typename BasePtrTy, typename Offset>
static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) {
  static_assert(std::is_integral<Offset>::value &&
                std::is_signed<Offset>::value,
                "offset type should be signed integer");

  auto base = reinterpret_cast<uintptr_t>(basePtr);
  // We want to do wrapping arithmetic, but with a sign-extended
  // offset. To do this in C, we need to do signed promotion to get
  // the sign extension, but we need to perform arithmetic on unsigned values,
  // since signed overflow is undefined behavior.
  auto extendOffset = (uintptr_t)(intptr_t)offset;
  return base + extendOffset;
}

最后返回的時候我們可以看到base + extendOffset;基地址加上偏移的值,最后得到真實的地址熬尺。

3.4.2 name

name的類型是TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false>,看著應(yīng)該和Parent差不多皂吮。我們點擊TargetRelativeDirectPointer跳轉(zhuǎn)到它的源碼處:

template <typename Runtime, typename Pointee, bool Nullable = true>
using TargetRelativeDirectPointer
  = typename Runtime::template RelativeDirectPointer<Pointee, Nullable>;

這里依舊是取別名蜂筹,繼續(xù)跳轉(zhuǎn)到RelativeDirectPointer艺挪,這里有兩個選擇口蝠,我們選擇相對引用那個(通過注釋區(qū)別)妙蔗。源碼如下:

/// A direct relative reference to an object that is not a function pointer.
template <typename T, bool Nullable, typename Offset>
class RelativeDirectPointer<T, Nullable, Offset,
    typename std::enable_if<!std::is_function<T>::value>::type>
    : private RelativeDirectPointerImpl<T, Nullable, Offset>
{
  using super = RelativeDirectPointerImpl<T, Nullable, Offset>;
public:
  using super::get;
  using super::super;
  
  RelativeDirectPointer &operator=(T *absolute) & {
    super::operator=(absolute);
    return *this;
  }

  operator typename super::PointerTy() const & {
    return this->get();
  }

  const typename super::ValueTy *operator->() const & {
    return this->get();
  }

  using super::isNull;
};

在源碼中我們可以看到很多關(guān)于supper的東西,有兩處都是通過get()方法返回的寸五,分別是PointerTyValueTy梳杏,下面我們就來到父類方法中看看。父類是RelativeDirectPointerImpl烁试,其部分源碼如下:

/// A relative reference to a function, intended to reference private metadata
/// functions for the current executable or dynamic library image from
/// position-independent constant data.
template<typename T, bool Nullable, typename Offset>
class RelativeDirectPointerImpl {
private:
  /// The relative offset of the function's entry point from *this.
  Offset RelativeOffset;
  
public:
  using ValueTy = T;
  using PointerTy = T*;
  
  PointerTy get() const & {
    // Check for null.
    if (Nullable && RelativeOffset == 0)
      return nullptr;
    
    // The value is addressed relative to `this`.
    uintptr_t absolute = detail::applyRelativeOffset(this, RelativeOffset);
    return reinterpret_cast<PointerTy>(absolute);
  }
}

這里同樣有一個Offset類型RelativeOffset,以及get()方法,同樣get()方法也會調(diào)用applyRelativeOffset函數(shù)進行地址的相加鄙才,關(guān)于applyRelativeOffset方法在介紹Parent的時候提到過攒庵。

這里面多了ValueTyPointerTy浓冒,ValueTy是傳入泛型的值闲擦,PointerTy是其指針墅冷。

name中還有個const char,這里是直接用const char類型存儲的類型的名稱腔彰。

3.4.4 AccessFunctionPtr

AccessFunctionPtr的類型是TargetRelativeDirectPointer<Runtime, MetadataResponse(...), /*Nullable*/ true>

name一樣的使用偏移指針TargetRelativeDirectPointer萍桌,只不過是const char替換成了MetadataResponse(...)上炎,點擊MetadataResponse跳轉(zhuǎn)后我們可以看到如下代碼:

MetadataResponse() : Metadata(nullptr) {}

/// A metadata response that might not be dynamically complete.
explicit MetadataResponse(llvm::Value *metadata, llvm::Value *state,
                        MetadataState staticLowerBoundState)
  : Metadata(metadata), DynamicState(state),
    StaticState(staticLowerBoundState) {
    assert(metadata && "must be valid");
}

所以這里只是一個Metadata的 指針。

3.4.5 Fields

Fields的類型是TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor, /*nullable*/ true>

TargetRelativeDirectPointer就不多說了裳食,這里看看FieldDescriptor點擊跳轉(zhuǎn)到其源碼處,部分源碼如下:

// Field descriptors contain a collection of field records for a single
// class, struct or enum declaration.
class FieldDescriptor {
  const FieldRecord *getFieldRecordBuffer() const {
    return reinterpret_cast<const FieldRecord *>(this + 1);
  }

public:
  const RelativeDirectPointer<const char> MangledTypeName;
  const RelativeDirectPointer<const char> Superclass;

  FieldDescriptor() = delete;

  const FieldDescriptorKind Kind;
  const uint16_t FieldRecordSize;
  const uint32_t NumFields;
  
}

這里有5個屬性:

  1. MangledTypeName
  2. Superclass
  3. kind
  4. FieldRecordSize
  5. NumFields

關(guān)于getFieldRecordBuffer函數(shù)的返回值FieldRecord源碼如下:

class FieldRecord {
  const FieldRecordFlags Flags;

public:
  const RelativeDirectPointer<const char> MangledTypeName;
  const RelativeDirectPointer<const char> FieldName;
.....  
}

FieldRecord主要是封裝了一些屬性救氯,用于存儲這些值。

3.4.6 NumFields

NumFields的類型是uint32_t這就沒什么好說的了甲抖,一個整形存儲屬性個數(shù)

3.4.7 FieldOffsetVectorOffset

FieldOffsetVectorOffset也是個uint32_t的整形挫剑,存儲偏移量暮顺。

3.5 Mirror取值

對于Description分析基本很透徹了捶码,那么我們就回到最初的位置,看看Mirror都是怎樣從Description取出相應(yīng)的值的祈纯。

3.5.1 type

首先我們看看type是怎么取的:

首先是調(diào)用swift_reflectionMirror_normalizedType函數(shù)

// func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
const Metadata *swift_reflectionMirror_normalizedType(OpaqueValue *value,
                                                      const Metadata *type,
                                                      const Metadata *T) {
  return call(value, T, type, [](ReflectionMirrorImpl *impl) { return impl->type; });
}

比如說這是個結(jié)構(gòu)體,此時的impl就是個StructImpl類型簇爆,所以這里的typeStructImpl父類ReflectionMirrorImpl的屬性type入蛆。

3.5.2 count

關(guān)于count的獲取首先是調(diào)用swift_reflectionMirror_count函數(shù)

// func _getChildCount<T>(_: T, type: Any.Type) -> Int
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
intptr_t swift_reflectionMirror_count(OpaqueValue *value,
                                      const Metadata *type,
                                      const Metadata *T) {
  return call(value, T, type, [](ReflectionMirrorImpl *impl) {
    return impl->count();
  });
}

同樣還以結(jié)構(gòu)體為例,此時的implStructImpl扼褪,內(nèi)部的count()函數(shù):

intptr_t count() {
    if (!isReflectable()) {
      return 0;
    }

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

這里的Struct就是個TargetStructMetadata類型,通過getDescription()函數(shù)獲取到一個TargetStructDescriptor類型的Description,然后取NumFields的值就是我們要的count跋核。

3.5.3 屬性名和屬性值

我們知道在Mirror中通過其初始化方法返回一個提供該值子元素的AnyCollection<Child>類型的children集合蹋订,Child是一個元組(label: String?, value: Any)露戒,label是一個可選類型的屬性名,value是屬性值荠锭。

在分析internalReflecting函數(shù)的時候证九,我們說children是懶加載的,而加載的時候會調(diào)用getChild方法拥坛,getChild方法源碼入下:

internal func getChild<T>(of value: T, type: Any.Type, index: Int) -> (label: String?, value: Any) {
  var nameC: UnsafePointer<CChar>? = nil
  var freeFunc: NameFreeFunc? = nil
  
  let value = _getChild(of: value, type: type, index: index, outName: &nameC, outFreeFunc: &freeFunc)
  
  let name = nameC.flatMap({ String(validatingUTF8: $0) })
  freeFunc?(nameC)
  return (name, value)
}

getChild方法中還會調(diào)用_getChild方法,源碼如下:

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

_getChild方法同樣是使用@_silgen_name修飾符最終調(diào)用的C++中的swift_reflectionMirror_subscript函數(shù)惨奕。

// We intentionally use a non-POD return type with this entry point to give
// it an indirect return ABI for compatibility with Swift.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
// func _getChild<T>(
//   of: T,
//   type: Any.Type,
//   index: Int,
//   outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
//   outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
// ) -> Any
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
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);
  });
}
#pragma clang diagnostic pop

這里我們可以看到是調(diào)用了implsubscript函數(shù),同樣以結(jié)構(gòu)體為例卧波,我們在StructImpl中找到該函數(shù),源碼如下:

AnyReturn subscript(intptr_t i, const char **outName,
                      void (**outFreeFunc)(const char *)) {
    auto fieldInfo = childMetadata(i, outName, outFreeFunc);

    auto *bytes = reinterpret_cast<char*>(value);
    auto fieldOffset = childOffset(i);
    auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);

    return copyFieldContents(fieldData, fieldInfo);
  }

通過subscript函數(shù)我們可以看到這里面還會調(diào)用childMetadata獲取到fieldInfo寸宏,其實這里就是獲取type氮凝,也就是屬性名罩阵,通過childOffset函數(shù)和index獲取到對于的偏移量,最后根據(jù)內(nèi)存偏移去到屬性值常摧。

childMetadata源碼:

const FieldType childMetadata(intptr_t i, const char **outName,
                                void (**outFreeFunc)(const char *)) {
    StringRef name;
    FieldType fieldInfo;
    std::tie(name, fieldInfo) = getFieldAt(type, i);
    assert(!fieldInfo.isIndirect() && "indirect struct fields not implemented");
    
    *outName = name.data();
    *outFreeFunc = nullptr;
    
    return fieldInfo;
  }

這里面的關(guān)鍵點是調(diào)用調(diào)用getFieldAt函數(shù)獲取屬性名稱,

getFieldAt源碼:

static std::pair<StringRef /*name*/, FieldType /*fieldInfo*/>
getFieldAt(const Metadata *base, unsigned index) {
  using namespace reflection;
  
  // If we failed to find the field descriptor metadata for the type, fall
  // back to returning an empty tuple as a standin.
  auto failedToFindMetadata = [&]() -> std::pair<StringRef, FieldType> {
    auto typeName = swift_getTypeName(base, /*qualified*/ true);
    missing_reflection_metadata_warning(
      "warning: the Swift runtime found no field metadata for "
      "type '%*s' that claims to be reflectable. Its fields will show up as "
      "'unknown' in Mirrors\n",
      (int)typeName.length, typeName.data);
    return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))};
  };

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

  auto *fields = baseDesc->Fields.get();
  if (!fields)
    return failedToFindMetadata();
  
  auto &field = fields->getFields()[index];
  // Bounds are always valid as the offset is constant.
  auto name = field.getFieldName();

  // Enum cases don't always have types.
  if (!field.hasMangledTypeName())
    return {name, FieldType::untypedEnumCase(field.isIndirectCase())};

  auto typeName = field.getMangledTypeName();

  SubstGenericParametersFromMetadata substitutions(base);
  auto typeInfo = swift_getTypeByMangledName(MetadataState::Complete,
   typeName,
   substitutions.getGenericArgs(),
   [&substitutions](unsigned depth, unsigned index) {
     return substitutions.getMetadata(depth, index);
   },
   [&substitutions](const Metadata *type, unsigned index) {
     return substitutions.getWitnessTable(type, index);
   });

  // If demangling the type failed, pretend it's an empty type instead with
  // a log message.
  if (!typeInfo.getMetadata()) {
    typeInfo = TypeInfo({&METADATA_SYM(EMPTY_TUPLE_MANGLING),
                         MetadataState::Complete}, {});
    missing_reflection_metadata_warning(
      "warning: the Swift runtime was unable to demangle the type "
      "of field '%*s'. the mangled type name is '%*s'. this field will "
      "show up as an empty tuple in Mirrors\n",
      (int)name.size(), name.data(),
      (int)typeName.size(), typeName.data());
  }

  auto fieldType = FieldType(typeInfo.getMetadata());
  fieldType.setIndirect(field.isIndirectCase());
  fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership());
  return {name, fieldType};
}

我們可以看到在上面這個方法中:

  • 首先通過getTypeContextDescriptor獲取baseDesc吸申,也就是我們說的Description

  • 然后通過Fields.get()獲取到fields

  • 接著通過getFields()[index]或取對應(yīng)的field

  • 最后通過getFieldName()函數(shù)獲取到屬性名稱

  • getTypeContextDescriptor函數(shù)在struct TargetMetadata中梳侨,

  • 通過這個函數(shù)獲取到一個TargetStructDescriptor走哺,它的父類的父類TargetTypeContextDescriptor中的Fields屬性

  • Fields屬性的類型TargetRelativeDirectPointer中有get方法

  • 實際中使用的FieldDescriptor類中getFieldRecordBuffer方法返回的FieldRecord中的getFieldName函數(shù)

getFields 源碼:

  const_iterator begin() const {
    auto Begin = getFieldRecordBuffer();
    auto End = Begin + NumFields;
    return const_iterator { Begin, End };
  }

  const_iterator end() const {
    auto Begin = getFieldRecordBuffer();
    auto End = Begin + NumFields;
    return const_iterator { End, End };
  }

  llvm::ArrayRef<FieldRecord> getFields() const {
    return {getFieldRecordBuffer(), NumFields};
  }

關(guān)于getFields我們可以看到這是一塊連續(xù)的空間,在beginend中:

  • begin就是getFieldRecordBuffer
  • getFieldRecordBuffer就是Begin + NumFields
  • 所以這就是一塊連續(xù)內(nèi)存的訪問

childOffset 源碼:

分析完了屬性名的獲取晒旅,我們來看看偏移量的獲取

intptr_t childOffset(intptr_t i) {
    auto *Struct = static_cast<const StructMetadata *>(type);

    if (i < 0 || (size_t)i > Struct->getDescription()->NumFields)
      swift::crash("Swift mirror subscript bounds check failure");

    // Load the offset from its respective vector.
    return Struct->getFieldOffsets()[i];
  }

這里面是調(diào)用TargetStructMetadata中的getFieldOffsets函數(shù)源碼如下:

/// Get a pointer to the field offset vector, if present, or null.
  const uint32_t *getFieldOffsets() const {
    auto offset = getDescription()->FieldOffsetVectorOffset;
    if (offset == 0)
      return nullptr;
    auto asWords = reinterpret_cast<const void * const*>(this);
    return reinterpret_cast<const uint32_t *>(asWords + offset);
  }

我們可以看到這里統(tǒng)一是通過獲取Description中的屬性,這里使用的屬性是FieldOffsetVectorOffset孝常。

獲取到偏移值后通過內(nèi)存偏移即可獲取到屬性值。

3.6 小結(jié)

至此我們對Mirror的原理基本探索完畢了岸梨,現(xiàn)在總結(jié)一下:

  1. Mirror通過初始化方法返回一會Mirror實例
  2. 這個實例對象根據(jù)傳入對象的類型去對應(yīng)的Metadata中找到Description
  3. Description可以獲取name也就是屬性的名稱
  4. 通過內(nèi)存偏移獲取到屬性值
  5. 還可以通過numFields獲取屬性的個數(shù)

下面通過該流程圖總結(jié)一下swift中的mirror對結(jié)構(gòu)體進行反射的主要流程

mirror流程圖

關(guān)于其他類型的反射也大同小異隔披,還有元組奢米、枚舉谒拴、類英上、元數(shù)據(jù)以及不透明類型的反射苍日,當然也有不完全支持反射的類型,比如結(jié)構(gòu)體就是不完全支持反射的類型豆茫,感興趣的可以繼續(xù)探索一下。

  • swift中的type(of:)火脉、dump(t)就是基于Mirror的反射原理來實現(xiàn)的
  • Swift中的json解析框架HandyJSON的主要原理與Mirror類似畸颅,本質(zhì)上就是利用metadata中的Description没炒,通過字段的訪問,做內(nèi)存的賦值种吸。

4. 仿寫 Mirror

為了加深對Mirror的理解坚俗,我們使用Swift語言仿寫一下。還是以結(jié)構(gòu)體為例辙浑。

4.1 TargetStructMetadata

首先我們需要擁有一個結(jié)構(gòu)體的元數(shù)據(jù)結(jié)構(gòu),這里我們命名為StructMetadata侠草,里面有繼承的kindDescriptor屬性,這里的Descriptor屬性是一個TargetStructDescriptor類型的指針功蜓。仿寫代碼如下:

struct StructMetadata{
    var kind: Int
    var Descriptor: UnsafeMutablePointer<StructDescriptor>
}

4.2 TargetStructDescriptor

4.1中我們用到的Descriptor屬性的內(nèi)部結(jié)構(gòu)現(xiàn)在也來實現(xiàn)一下。這個是Mirror中用到很多的屬性著隆。對于結(jié)構(gòu)體來說其內(nèi)部有7個屬性

  1. flag是個32位的整形弦赖,我們用Int32代替
  2. parent是記錄父類的蹬竖,類型是TargetRelativeContextPointer<Runtime>,這里也可以用Int32代替
  3. name記錄類型的流酬,它的類型是TargetRelativeDirectPointer<char>案腺,所以我們需要實現(xiàn)一個TargetRelativeDirectPointer
  4. AccessFunctionPtrname類似,內(nèi)部是個指針
  5. Fields也與name類似康吵,內(nèi)部是個FieldDescriptor
  6. NumFields使用Int32
  7. FieldOffsetVectorOffset也是用Int32

仿寫實現(xiàn)如下:

struct StructDescriptor {
    let flags: Int32
    let parent: Int32
    var name: RelativePointer<CChar>
    var AccessFunctionPtr: RelativePointer<UnsafeRawPointer>
    var Fields: RelativePointer<FieldDescriptor>
    var NumFields: Int32
    var FieldOffsetVectorOffset: Int32
}

4.3 TargetRelativeDirectPointer

  • TargetRelativeDirectPointerRelativeDirectPointer的別名访递,其內(nèi)部有一個繼承的RelativeOffset屬性晦嵌,是int32_t類型,我們可以用Int32代替拷姿。
  • 還有一個get方法惭载。內(nèi)部通過指針偏移獲取值

仿寫實現(xiàn)如下:

struct RelativePointer<T> {
    var offset: Int32

    mutating func get() -> UnsafeMutablePointer<T>{
        let offset = self.offset

        return withUnsafePointer(to: &self) { p in
            return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
        }
    }
}

4.4 FieldDescriptor

FieldDescriptorMirror反射中有著很重要的作用,其內(nèi)部有5個屬性:

  1. MangledTypeNameRelativeDirectPointer<const char>類型响巢,我們使用RelativePointer<CChar>代替
  2. SuperclassMangledTypeName一樣
  3. kindFieldDescriptorKind類型描滔,實際是uint16_t,這里我們使用UInt16代替
  4. fieldRecordSizeuint16_t也使用使用UInt16代替
  5. numFields使用Int32代替
  6. fields踪古,其實從屬性上看不到有這個含长,但是這里面有個getFieldRecordBuffer方法,通過this+1的方式一個一個的訪問屬性伏穆,所以這是一塊連續(xù)的內(nèi)存空間拘泞,我們使用fields代替

仿寫代碼如下:

struct FieldDescriptor {
    var MangledTypeName: RelativePointer<CChar>
    var Superclass: RelativePointer<CChar>
    var kind: UInt16
    var fieldRecordSize: Int16
    var numFields: Int32
    var fields: FieldRecord //連續(xù)的存儲空間
}

4.5 FieldRecord

FieldRecord存儲著屬性的相關(guān)信息,其內(nèi)部有三個屬性

  1. FlagsFieldRecordFlags類型實際是uint32_t枕扫,這里我們使用Int32代替
  2. MangledTypeName使用RelativePointer<CChar>代替
  3. FieldName使用RelativePointer<CChar>代替

仿寫帶如下:

struct FieldRecord {
    var Flags: Int32
    var MangledTypeName: RelativePointer<CChar>
    var FieldName: RelativePointer<CChar>
}

4.6 測試

下面我們使用內(nèi)存綁定的計數(shù)訪問一個結(jié)構(gòu)體

定義一個結(jié)構(gòu)體:

struct Person {
    var name: String = "xiaohei"
    var age: Int = 18
    var height = 1.85
}

var p = Person()

4.6.1 綁定結(jié)構(gòu)體內(nèi)存

使用unsafeBitCast按位強轉(zhuǎn)陪腌,將Person綁定到StructMetadata上,這個操作非常危險烟瞧,沒有任何校驗和修飾

let ptr = unsafeBitCast(Person.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)

4.6.2 打印類型和屬性個數(shù)

下面我們就打印一下結(jié)構(gòu)體的類型(也就是它的名稱)和其中屬性的個數(shù):

let namePtr = ptr.pointee.Descriptor.pointee.name.get()

print(String(cString: namePtr))
print(ptr.pointee.Descriptor.pointee.NumFields)

打印結(jié)果:

image

這里我們就可以看到結(jié)構(gòu)體的名稱和其屬性個數(shù)的正確打印了诗鸭。

4.6.3 打印屬性名稱

下面我們就來打印一下屬性的名稱,首先是獲取到FieldDescriptor的指針参滴,然后通過內(nèi)存偏移的方式訪問每一個FieldRecord强岸,最后在訪問FieldRecord中的屬性名。代碼如下:

let fieldDescriptorPtr = ptr.pointee.Descriptor.pointee.Fields.get()

let recordPtr = withUnsafePointer(to: &fieldDescriptorPtr.pointee.fields) {
    return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: FieldRecord.self).advanced(by: 2))
}

print(String(cString: recordPtr.pointee.FieldName.get()))

打印結(jié)果:

image

此時我們就可以看到第三屬性height的打印卵洗,如果advanced(by: 0)則打印第一個屬性请唱,以此類推弥咪。

image

4.6.1 打印屬性值

下面我們訪問一下屬性值:

首先是獲取屬性偏移量的數(shù)組,也就是getFieldOffsets函數(shù)返回的值十绑。根據(jù)源碼:

  • 首先獲取FieldOffsetVectorOffset的值
  • 然后在加上this也就是當前Metadata的指針
  • 這里我們將仿寫的StructMetadata的指針ptr重綁定為Int
  • 源碼中加上FieldOffsetVectorOffset聚至,這里我們就移動FieldOffsetVectorOffset
  • 然后將上述移動后的綁定為一個Int32的指針
  • 最后使用UnsafeBufferPointer和屬性個數(shù)創(chuàng)建一個buffer數(shù)組指針
  • 接下來我們就可以從數(shù)組中取出每個屬性的偏移值
  • 然后取出結(jié)構(gòu)體實例p的內(nèi)存地址
  • 然后按照buffer數(shù)組中的偏移值進行偏移,重綁定為屬性的類型
  • 最后就可以打印出屬性值了

實現(xiàn)代碼:

var bufferPtr = UnsafeBufferPointer(start: UnsafeRawPointer(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self).advanced(by: numericCast(ptr.pointee.Descriptor.pointee.FieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self), count: Int(ptr.pointee.Descriptor.pointee.NumFields))

var fieldOffset = bufferPtr[2]

var valuePtr = withUnsafeMutablePointer(to: &p) { $0 }

var bufferPtr1 = UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(bufferPtr[0]))).assumingMemoryBound(to: String.self)
print(bufferPtr1.pointee)

var bufferPtr2 = UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(bufferPtr[1]))).assumingMemoryBound(to: Int.self)
print(bufferPtr2.pointee)

var bufferPtr3 = UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(bufferPtr[2]))).assumingMemoryBound(to: Double.self)
print(bufferPtr3.pointee)

打印結(jié)果:

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末本橙,一起剝皮案震驚了整個濱河市扳躬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌甚亭,老刑警劉巖贷币,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異亏狰,居然都是意外死亡役纹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門暇唾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來促脉,“玉大人,你說我怎么就攤上這事策州∪澄叮” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵够挂,是天一觀的道長旁仿。 經(jīng)常有香客問我,道長孽糖,這世上最難降的妖魔是什么枯冈? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮梭姓,結(jié)果婚禮上霜幼,老公的妹妹穿的比我還像新娘。我一直安慰自己誉尖,他們只是感情好罪既,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铡恕,像睡著了一般琢感。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上探熔,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天驹针,我揣著相機與錄音,去河邊找鬼诀艰。 笑死柬甥,一個胖子當著我的面吹牛饮六,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苛蒲,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼卤橄,長吁一口氣:“原來是場噩夢啊……” “哼辱志!你這毒婦竟也來了评凝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤袋马,失蹤者是張志新(化名)和其女友劉穎漏健,沒想到半個月后嚎货,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡蔫浆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年殖属,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓦盛。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡忱辅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谭溉,到底是詐尸還是另有隱情,我是刑警寧澤橡卤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布扮念,位于F島的核電站,受9級特大地震影響碧库,放射性物質(zhì)發(fā)生泄漏柜与。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一嵌灰、第九天 我趴在偏房一處隱蔽的房頂上張望弄匕。 院中可真熱鬧,春花似錦沽瞭、人聲如沸迁匠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽城丧。三九已至,卻和暖如春豌鹤,著一層夾襖步出監(jiān)牢的瞬間亡哄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工布疙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蚊惯,地道東北人愿卸。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像截型,于是被迫代替她去往敵國和親趴荸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

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