Swift Mirror原理解析

前言

上篇Swift Mirror & Error主要是講解了Mirror的一個(gè)常見(jiàn)的應(yīng)用場(chǎng)景:JSON解析,但是里面的原理是怎樣的?底層源碼流程是如何處理反射呢?本篇文章將為大家詳細(xì)解析Mirror的底層實(shí)現(xiàn)流程。

一、Mirror架構(gòu)大致分析

首先我們大致來(lái)看看Mirror的架構(gòu),大概有哪些部分構(gòu)成??

  1. Mirror.swift源碼路徑??

swift->stdlib->public->core->Mirror.swift

2.與反射相關(guān)的API

  • ReflectionMirror.swift
  • ReflectionMirror.mm

其中,ReflectionMirror.swift中定義的函數(shù),會(huì)通過(guò)@_silgen_name修飾符,這個(gè)修飾符的作用就是Swift編譯器會(huì)將該修飾符修飾的函數(shù)符號(hào)映射成C++的函數(shù)符號(hào)。

@_silgen_name示例

通常,我們?cè)趕wift的工程中想調(diào)用C/C++的函數(shù),一般是這樣處理(我們以C函數(shù)為例看看):

  1. 先在.h中聲明c函數(shù),在.c中實(shí)現(xiàn)
  1. 在橋接.h文件中引入C函數(shù)的頭文件.h
  1. 在.swift中使用C函數(shù)

其實(shí),我們也可以省去第2步,在.swift中使用@_silgen_name修飾符??

@_silgen_name("add")
func swift_add(_ a :Int32, _ b :Int32) -> Int32

var value = swift_add(10, 20)
print(value)

二、Mirror底層流程解析

接下來(lái),我們看看Mirror的源碼??

接著,我們看internalReflecting源碼,搜索internalReflecting,發(fā)現(xiàn)是在ReflectionMirror.swift中??

2.1 internalReflecting源碼分析

再來(lái)看初始化internalReflecting源碼中調(diào)用的幾個(gè)關(guān)鍵函數(shù):

_getNormalizedType

根據(jù)上面對(duì)Mirror的初始化函數(shù)的分析得知,_getNormalizedType是獲取當(dāng)前對(duì)象的類型??

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

對(duì)應(yīng)的C++函數(shù)是swift_reflectionMirror_normalizedType??

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

調(diào)用的是call。

getChild

再來(lái)看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)
}

其中最關(guān)鍵的就是let value = _getChild(of: value, type: type, index: index, outName: &nameC, outFreeFunc: &freeFunc),繼續(xù)看看_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

同理,所對(duì)應(yīng)的C++函數(shù)是swift_reflectionMirror_subscript??

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

最終也是調(diào)用的call

_getDisplayStyle

_getDisplayStyle是獲取當(dāng)前對(duì)象subject的顯示類型,源碼如下??

@_silgen_name("swift_reflectionMirror_displayStyle")
internal func _getDisplayStyle<T>(_: T) -> CChar

同理,搜索swift_reflectionMirror_displayStyle??

// func _getDisplayStyle<T>(_: T) -> CChar
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
char swift_reflectionMirror_displayStyle(OpaqueValue *value, const Metadata *T) {
  return call(value, T, nullptr, [](ReflectionMirrorImpl *impl) { return impl->displayStyle(); });
}

還是call。

最關(guān)鍵的call函數(shù)

call函數(shù)的源碼很長(zhǎng),我們一部分一部分的看??

  • 入?yún)?& 返回值
template<typename F>
auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedType,
          const F &f) -> decltype(f(nullptr))
  1. passedValue ?? 一個(gè)指針,指向swift調(diào)用方傳遞來(lái)的值
  2. T ?? 該值的靜態(tài)類型
  3. passedType ?? 顯式傳入的且在反射過(guò)程中的用到的類型
  4. f ?? 傳遞被查找到的會(huì)被調(diào)用的實(shí)現(xiàn)的引用對(duì)象,可以理解是個(gè)閉包的引用對(duì)象
  5. 返回值 ?? 返回f參數(shù)調(diào)用后的返回值
  • 部分一:確定反射的真實(shí)類型

其中unwrapExistential源碼(其實(shí)就是強(qiáng)制類型轉(zhuǎn)換,并取值)??

ReflectionMirrorImpl后面再分析。

  • 部分二:針對(duì)【類對(duì)象】的處理

*部分三:其它類型的處理

以上就是對(duì)call函數(shù)源碼的大致流程的分析,最關(guān)鍵的一步就是使用ReflectionMirrorImpl子類的實(shí)例去調(diào)用f,然后封裝成一個(gè)匿名函數(shù)call(類似于閉包),再根據(jù)type對(duì)應(yīng)的類型type->getKind()去走真正的反射流程。

2.2 調(diào)試驗(yàn)證:_getNormalizedType

上面的源碼看起來(lái)是不是有些費(fèi)勁,沒(méi)關(guān)系,接下來(lái)我們來(lái)斷點(diǎn)調(diào)試一下_getNormalizedType函數(shù),看看傳遞的入?yún)⒕唧w是什么?

先分別給swift_reflectionMirror_normalizedTypecall打上斷點(diǎn),如下圖所示:

運(yùn)行源碼,再準(zhǔn)備調(diào)試示例代碼,輸入到終端??

class LGTeacher{var age = 18}
var t = LGTeacher()
let mirror = Mirror(reflecting: t)

觸發(fā)斷點(diǎn)如下圖??(其中var t = LGTeacher()這句也會(huì)觸發(fā),直接跳過(guò))

注意:swift源碼調(diào)試起來(lái)很卡,經(jīng)常容易中斷??

2.3 ReflectionMirrorImpl 反射子類

ReflectionMirrorImpl的子類主要有以下幾種:

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

首先查看ReflectionMirrorImpl的底層定義

我們以類ClassImpl為例,看看??

ClassImpl與其它數(shù)據(jù)結(jié)構(gòu)的不同在于,需要考慮繼承鏈關(guān)系,于是多出了父類遞歸處理的一些函數(shù)。其實(shí)仔細(xì)看看類ClassImpl反射子類中對(duì)屬性的操作處理,都是先找到metadata,然后找到其description,再根據(jù)偏移值fieldOffset,就可得到真正索引i對(duì)應(yīng)的字段(即屬性)。

ClassMetadata

其中,類Classmetadata類型就是ClassMetadata??

ClassMetadata定義??

using ClassMetadata = TargetClassMetadata<InProcess>;
??
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
...
}

??
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
...
}

??
struct TargetHeapMetadata : TargetMetadata<Runtime> {
...
}

根據(jù)繼承鏈,最終定位到我們熟悉的TargetMetadata,之前在Swift編譯流程 & Swift類中分析過(guò),TargetMetadata中有一個(gè)屬性kind(相當(dāng)于OC中的isa),而TargetClassMetadata除了擁有父類的kind,還有一個(gè)description,用于記錄元數(shù)據(jù)的描述??

TargetClassDescriptor

接著來(lái)到TargetClassDescriptor??


TargetClassDescriptor有關(guān)鍵的兩個(gè)成員變量NumFieldsFieldOffsetVectorOffset,同時(shí)繼承鏈的父類包含TargetTypeContextDescriptor??

繼續(xù)沿著繼承鏈向上查找,來(lái)到TargetContextDescriptor??

template <typename Runtime>
class TargetTypeContextDescriptor : public TargetContextDescriptor<Runtime>

其中包含2個(gè)重要成員變量FlagsParent??

至此,結(jié)合上面的對(duì)ClassMetadataTargetClassDescriptor這兩個(gè)關(guān)鍵類型的分析,我們?cè)诜瓷渲袑?duì)屬性的處理是通過(guò)getFieldAt函數(shù)??

上圖可知,getFieldAt中是通過(guò)Metadata獲取Fields字段,進(jìn)而getFieldName得到字段名稱,而Fields的類型TargetRelativeDirectPointer,其內(nèi)部的描述信息類型是FieldDescriptor。

偏移量的計(jì)算

上述已拿到了屬性名稱,剩下的就是屬性值的處理,上述已經(jīng)分析過(guò),屬性的是通過(guò)偏移量相加來(lái)計(jì)算的,而這個(gè)偏移量也是存儲(chǔ)在TargetRelativeDirectPointer指針中(也是Fields中),同理,根據(jù)繼承鏈,向上查找??

using TargetRelativeDirectPointer
  = typename Runtime::template RelativeDirectPointer<Pointee, Nullable>;
??
class RelativeDirectPointer<T, Nullable, Offset,
    typename std::enable_if<!std::is_function<T>::value>::type>
    : private RelativeDirectPointerImpl<T, Nullable, Offset> {
...
}
??
template<typename T, bool Nullable, typename Offset>
class RelativeDirectPointerImpl {
private:
  /// The relative offset of the function's entry point from *this.
  Offset RelativeOffset;
...
}

最終定位到屬性RelativeOffset,用于表示屬性的相對(duì)偏移值,而不是直接存儲(chǔ)地址,如下圖所示??

偏移的計(jì)算相關(guān)的底層源碼??

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

// 其中,applyRelativeOffset的源碼 ??
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;
  // 指針地址+存放的offset(偏移地址) --> 內(nèi)存平移獲取值
  return base + extendOffset;
}
FieldDescriptor類:存放屬性

最后我們來(lái)看看FieldDescriptor的源碼??

class FieldDescriptor {
  const FieldRecord *getFieldRecordBuffer() const {
    return reinterpret_cast<const FieldRecord *>(this + 1);
  }

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

    ......

  const FieldDescriptorKind Kind;
  const uint16_t FieldRecordSize;
  const uint32_t NumFields;
  
    ......
  
  // 獲取所有屬性,每個(gè)屬性用FieldRecord封裝
   llvm::ArrayRef<FieldRecord> getFields() const {
    return {getFieldRecordBuffer(), NumFields};
  }
  
  ......
} 

其中,FieldRecord是對(duì)屬性的一個(gè)封裝,定義??

class FieldRecord {
  const FieldRecordFlags Flags;

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

三、仿寫Mirror

以上主要分析了類Class通過(guò)Mirror反射獲取屬性和值,還有涉及的重要的類和結(jié)構(gòu)體的相關(guān)源碼?,F(xiàn)在我們以結(jié)構(gòu)體為例,仿寫Mirror代碼??

// 結(jié)構(gòu)體類型
struct StructMetadata {
    var kind:        Int
    var description: UnsafeMutablePointer<StructMetadataDesc>
}

// 結(jié)構(gòu)體類型的描述
struct StructMetadataDesc {
    var flags:                   UInt32
    var parent:                  UInt32  // 展示用Uint32代替,實(shí)際是相同大小的結(jié)構(gòu)體,
    var name:                    RelativeDirectPointer<CChar>  // 不在乎具體類型,就先用UnsafeRawPointer
    var accessFunctionPtr:       RelativeDirectPointer<UnsafeRawPointer>  // 不在乎具體類型,就先用UnsafeRawPointer
    var fields:                  RelativeDirectPointer<FieldDescription>  // 記錄所有屬性內(nèi)容
    var numFields:               UInt32   // 屬性個(gè)數(shù)
    var fieldOffsetVectorOffset: UInt32
}

// 記錄結(jié)構(gòu)體內(nèi)所有屬性的結(jié)構(gòu)
struct FieldDescription {
    var MangledTypeName:         RelativeDirectPointer<CChar>
    var Superclass:              RelativeDirectPointer<CChar>
    var Kind:                    UInt16
    var FieldRecordSize:         UInt16
    var NumFields:               UInt32
    var fields:                  FieldRecord // 連續(xù)存儲(chǔ)空間 (有幾個(gè)數(shù)據(jù),就會(huì)在后面添加幾個(gè)記錄,通過(guò)內(nèi)存平移讀取)
}

// 每個(gè)屬性的內(nèi)容
struct FieldRecord {
    var flag:                    Int32
    var mangledTypeName:         RelativeDirectPointer<CChar>
    var fieldName:               RelativeDirectPointer<CChar>   // 屬性名稱
}

// 相對(duì)位移指針
struct RelativeDirectPointer<T>{
    var offset: Int32

    // 偏移offset位置,獲取內(nèi)容指針
    mutating func get() -> UnsafeMutablePointer<T> {
        let offset = self.offset
        // withUnsafePointer獲取指針
        return withUnsafePointer(to: &self) { p in
            // UnsafeMutablePointer 返回T類型對(duì)象的指針
            // UnsafeRawPointer將p指針轉(zhuǎn)換為未知類型
            // numericCast將offset轉(zhuǎn)換為偏移單位數(shù)
            // advanced進(jìn)行內(nèi)存偏移
            // assumingMemoryBound綁定指針為T類型
            return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
        }

    }
}

struct LGTeacher {
    var age = 18
    var name = "Luoji"
}

// 讀取將LGTeacher指針內(nèi)容,賦值給StructMetadata  (unsafeBitCast: 通過(guò)字節(jié)讀取)
let p = unsafeBitCast(LGTeacher.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)

// 讀取當(dāng)前name的內(nèi)容指針
let namePtr = p.pointee.description.pointee.name.get()
let count = p.pointee.description.pointee.numFields
print("類型:\(String(cString: namePtr))") // name是CChar類型,轉(zhuǎn)為字符串輸出
print("屬性個(gè)數(shù):\(count)")

// 單獨(dú)讀取第一個(gè)屬性名
var fields = p.pointee.description.pointee.fields.get()
let fieldRecord1Name = fields.pointee.fields.fieldName.get()
print(String(cString: fieldRecord1Name))

// 讀取所有記錄
print("----讀取所有屬性名----")
(0..<count).forEach { index in
    let recordPtr = withUnsafePointer(to: &fields.pointee.fields) {
        return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: FieldRecord.self).advanced(by: Int(index)))
    }
    print(String(cString: recordPtr.pointee.fieldName.get()))
}

print("----dump----")
dump(LGTeacher()) // 相似的實(shí)現(xiàn)方式

總結(jié)

綜上所述,Mirror反射干的事情大致分為三步:

  1. Mirror在實(shí)例對(duì)象的metadata中找到Descriptor
  2. Descriptor
    2.1 找到name,獲取類型(相當(dāng)于type名稱)
    2.2 找到numFields,獲取屬性個(gè)數(shù)
  3. 找到FieldDescriptor中的fields,來(lái)找到對(duì)當(dāng)前屬性描述,然后通過(guò)指針內(nèi)存平移,獲取其他屬性
    下圖是以為例,底層Mirror反射的流程圖??
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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