Swift 五、Mirror源碼分析 & StructMetadata還原

Mirror源碼分析.png

一、Mirror的基本用法

所謂反射就是可以動(dòng)態(tài)獲取類型、成員信息,在運(yùn)?時(shí)可以調(diào)??法、屬性等?為的特性。在使?OC開發(fā)時(shí)很少?gòu)?qiáng)調(diào)其反射概念,因?yàn)镺C的Runtime要?其他語(yǔ)?中的反射強(qiáng)?的多。但是 Swift 是??類型安全的語(yǔ)?,不?持我們像 OC 那樣直接操作,它的標(biāo)準(zhǔn)庫(kù)仍然提供了反射機(jī)制來(lái)讓我們?cè)L問成員信息。
Swift 的反射機(jī)制是基于?個(gè)叫 Mirror 的結(jié)構(gòu)體來(lái)實(shí)現(xiàn)的。為具體的實(shí)例創(chuàng)建?個(gè)Mirror對(duì)象,然后就可以通過(guò)它查詢這個(gè)實(shí)例。

class ZGTeacher {
    var age: Int = 18
    func teach() {
        print("teach")
    }
}

///?先通過(guò)構(gòu)造?法構(gòu)建?個(gè)Mirror實(shí)例,這?傳?的參數(shù)是 Any,也就意味著當(dāng)前可以是類,結(jié) 構(gòu)體,枚舉等
let mirror = Mirror(reflecting: ZGTeacher())

///接下來(lái)遍歷 children 屬性,這是?個(gè)集合
for child in mirror.children {
    ///然后我們可以直接通過(guò) label 輸出當(dāng)前的名稱,value 輸出當(dāng)前反射的值
    print("\(String(describing: child.label)):\(child.value)")
}

lldb打印

Optional("age"):18

實(shí)際使用案例

class ZGTeacher {
    var age: Int = 18
    func teach() {
        print("teach")
    }
}

func test(_ mirrorObj: Any) -> Any {
    let mirror = Mirror(reflecting: mirrorObj)
    guard  !mirror.children.isEmpty  else { return mirrorObj }
    var result: [String : Any] = [ : ]
    for child in mirror.children {
        if let key = child.label {
            result[key] = test(child.value)
        } else {
            print("No Keys")
        }
    }
    return result
    
}

var reslut = test(ZGTeacher())
print(reslut)

我們想讓我們的函數(shù)不管是結(jié)構(gòu)體,類還是枚舉還是基礎(chǔ)類型也好,都能夠具備這個(gè)?法。這個(gè)時(shí)候我們是不是就可以?協(xié)議來(lái)做?什么意思那?

首先,我們定義一個(gè)協(xié)議,這里就表示如果想要具備模型轉(zhuǎn)字典的方式,那么這里就要遵循這個(gè)協(xié)議,然后實(shí)現(xiàn)jsonMap的方法。

protocol ZGJsonMap {
    func jsonMap() -> Any
}

但是大家想一下,這個(gè)test方法里面的功能是不是通用的啊,也就意味著我們不需要每一個(gè)遵循了ZGJsonMap協(xié)議的都自己實(shí)現(xiàn),所以這里我們是不是說(shuō)可以給這個(gè)協(xié)議一個(gè)默認(rèn)的實(shí)現(xiàn)啊。

class ZGTeacher {
    var age: Int = 18
    func teach() {
        print("teach")
    }
}

enum JSONMapError: Error {
    case emptyKey
    case notConformProtocol
}

protocol JSONMap {
  func jsonMap() throws -> Any
}


extension JSONMap {
  func jsonMap() throws -> Any {
    let mirror = Mirror(reflecting: self)

    guard !mirror.children.isEmpty else { return self }

    var result: [String : Any] = [:]

    for child in mirror.children {
       if let value = child.value as? JSONMap {
         if let key = child.label {
            result[key] = try? value.jsonMap()
          } else {
            return JSONMapError.emptyKey
          }
       } else {
           return JSONMapError.notConformProtocol
       }
    }

    return result
  }
    
}

///我們需要對(duì)當(dāng)前的常見類型和自定義類型遵循JSONMap的協(xié)議
///這樣對(duì)于沒有value才能嵌套解析
extension ZGTeacher: JSONMap{}
extension Int: JSONMap{}
extension String: JSONMap{}

var t = ZGTeacher()

var t1 = try? t.jsonMap()
print(t1!)

二、Mirror源碼解析

?先我們先在源?件??搜索 Mirror.Swift ,在源碼中我們可以很清晰的看到 Mirror 是由結(jié)構(gòu)體實(shí)現(xiàn)的,我們忽略掉?些細(xì)節(jié),快速定位到初始化的?法。

public init(reflecting subject: Any) {
    if case let customized as CustomReflectable = subject {
      self = customized.customMirror
    } else {
      self = Mirror(internalReflecting: subject)
    }
  }

可以看到,這?接受?個(gè) Any 類型的參數(shù),同樣的這?有?個(gè) if case 的寫法來(lái)判斷當(dāng)前的 subject 是否遵循了customReflectable 協(xié)議,如果是我們就直接調(diào)? customMirror , 否則就進(jìn)?下級(jí)函數(shù)的調(diào)?。
這?有兩個(gè)需要注意的點(diǎn): if case 的寫法,這?其實(shí)枚舉 case 的模式匹配,和我們的 Switch ?樣,這?是只有?個(gè) case 的 switch 語(yǔ)句。與此同時(shí)這?出現(xiàn)了?個(gè) customRefletable 的協(xié)議。
這?我們看?下具體?法: ?先我們遵循 customReflectable協(xié)議,并實(shí)現(xiàn)其中的屬性 customMirrorcustomMirror會(huì)返回?個(gè) Mirror 對(duì)象。代碼展示如下:

class ZGTeacher: CustomReflectable {
    var age: Int
    var name: String
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }

    var customMirror: Mirror {
        let info = KeyValuePairs<String, Any>.init(dictionaryLiteral: ("age", age),("name", name))
        let mirror = Mirror.init(self, children: info, displayStyle: .class, ancestorRepresentation: .generated)
        return mirror
    }
}
var t = ZGTeacher(age: 18, name: "zhang")
print("end")

實(shí)現(xiàn)這個(gè) CustomReflectable 最直觀的區(qū)別在于我們?cè)?lldb debug 中會(huì)出現(xiàn)更詳細(xì)的 debug 信息。

po t
? <ZGTeacher: 0x1007405a0>
  - age : 18
  - name : "zhang"

回到我們的源碼當(dāng)中,對(duì)字符串 Mirror(internalReflecting 在源碼當(dāng)中進(jìn)?檢索,我們 就能夠快速定位到 ReflectionMirror.swift ?件

extension Mirror {
  internal init(internalReflecting subject: Any,
              subjectType: Any.Type? = nil,
              customAncestor: Mirror? = nil)
  {
///subject:類型信息
    let subjectType = subjectType ?? _getNormalizedType(subject, type: type(of: subject)) 

在當(dāng)前文件中搜索getNormalizedType

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

這?使?了?個(gè)編譯器字段 @_silgen_name ,其實(shí)是Swift的?個(gè)隱藏符號(hào),作?是將某個(gè)C/C++語(yǔ)?函數(shù)直接映射為Swift函數(shù)。
什么意思那?我們的 Swift 是可以和C語(yǔ)?進(jìn)?交互的,?如我們?cè)贑中定義?個(gè)函數(shù):

int zg_add(int a, int b) {
    return  a + b;
}

接下來(lái)如果我們的 Swift 想要使?這個(gè)函數(shù),我們要怎么做??先要在 .h 頭?件中暴露這個(gè)函數(shù):

截屏2022-01-18 17.19.55.png

然后在當(dāng)前的橋接頭?件當(dāng)中暴露


截屏2022-01-18 17.19.44.png

這樣我們就能在swift里直接使用了。


截屏2022-01-18 17.22.25.png

是不是?較麻煩,這?我們把頭?件全部都刪掉:
@_silgen_name("zg_add")
func swift_zg_add(a: Int32, b: Int32) -> Int32
var value = swift_zg_add(a: 10, b: 20)
print(value)

這里一樣可以正常調(diào)用,是不是很簡(jiǎn)潔方便哪?
回到我們的源代碼,搜索getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type 最終調(diào)?的是 ReflectionMirror.cpp 中的 C++ 代碼。

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

我們往上翻?翻就能夠找到 call 函數(shù)的實(shí)現(xiàn),這?其實(shí)是?個(gè)回調(diào)函數(shù),當(dāng)前回調(diào)的具體數(shù)據(jù)都是由 ReflectionMirrorImpl 結(jié)構(gòu)體實(shí)現(xiàn)。

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

ReflectionMirrorImpl 結(jié)構(gòu)體的具體實(shí)現(xiàn)(可以看到這是?個(gè)抽象類,也就意味著不同類型的反射都需要去實(shí)現(xiàn) ReflectionMirrorImpl ),這?我們?cè)谙?的代碼中也能看到 class, struct, enum, Tuple的具體實(shí)現(xiàn)。

truct 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() {}
};

這?我們?先以 struct 為例來(lái)看?下 Mirror 都是如何獲取到這些數(shù)據(jù)的。
當(dāng)前的屬性數(shù)量 (可以看到的是,這?通過(guò) Metadata 中的 getDescription查詢字段 NumFields )

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

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

其中 getDescription()

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

又?如具體屬性的獲取

AnyReturn subscript(intptr_t i, const char **outName,
                      void (**outFreeFunc)(const char *)) override {
    unsigned tag;
    const Metadata *payloadType;
    bool indirect;

    auto *caseName = getInfo(&tag, &payloadType, &indirect);

    // Copy the enum itself so that we can project the data without destroying
    // the original.
    Any enumCopy;
    auto *enumCopyContainer
      = type->allocateBoxForExistentialIn(&enumCopy.Buffer);
    type->vw_initializeWithCopy(enumCopyContainer,
                                const_cast<OpaqueValue *>(value));

    // Copy the enum payload into a box
    const Metadata *boxType = (indirect ? &METADATA_SYM(Bo).base : payloadType);
    BoxPair pair = swift_allocBox(boxType);
    type->vw_destructiveProjectEnumData(enumCopyContainer);
    boxType->vw_initializeWithTake(pair.buffer, enumCopyContainer);
    type->deallocateBoxForExistentialIn(&enumCopy.Buffer);
    
    value = pair.buffer;

    // If the payload is indirect, we need to jump through the box to get it.
    if (indirect) {
      const HeapObject *owner = *reinterpret_cast<HeapObject * const *>(value);
      value = swift_projectBox(const_cast<HeapObject *>(owner));
    }
    
    *outName = caseName;
    *outFreeFunc = nullptr;
    
    Any result;

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

    swift_release(pair.object);
    return AnyReturn(result);
  }

可以看到是這?通篇都是通過(guò) Metadata , getDescription() , FieldDescrition 這?個(gè)東?來(lái)去實(shí)現(xiàn)的,?個(gè)是當(dāng)前類型的元數(shù)據(jù),?個(gè)是當(dāng)前類型的描述,?個(gè)是對(duì)當(dāng)前類型屬性的描述。所以看到這?我們能夠明? Mirror 是如何?作的。
同時(shí)我們?cè)谇?的探索過(guò)程中也探索了類,結(jié)構(gòu)體和枚舉,接下來(lái)我們嘗試能不能??利? Metadata 來(lái)還原獲取當(dāng)前類型的各種屬性。

三、StructMetadata源碼分析

3.1 TargetStructMetadata

  • 首先我們搜索TargetStructMetadata,找到TargetStructMetadata的定義
template <typename Runtime>
struct TargetStructMetadata : public TargetValueMetadata<Runtime> {
    /// 此處會(huì)返回一個(gè)TargetStructDescriptor類型的Description
  const TargetStructDescriptor<Runtime> *getDescription() const {
    return llvm::cast<TargetStructDescriptor<Runtime>>(this->Description);
  }

};

TargetStructMetadata.png

可以看出TargetStructMetadata繼承自TargetValueMetadata

  • 搜索TargetValueMetadata,找到TargetValueMetadata的定義
struct TargetValueMetadata : public TargetMetadata<Runtime> {
  
  TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> *
  __ptrauth_swift_type_descriptor> Description;

};

TargetValueMetadata.png

我們只看到有一個(gè)Description屬性,它的類型是TargetValueTypeDescriptor。
TargetValueMetadata 繼承自TargetMetadata

  • 搜索TargetMetadata,找到TargetMetadata的定義
struct TargetMetadata {
  
private:
  /// The kind. Only valid for non-class metadata; getKind() must be used to get
  /// the kind value.
  StoredPointer Kind;

};

TargetMetadata.png

我們只看到它有一個(gè)Kind屬性。

3.2 TargetValueTypeDescriptor

  • 首先我們根據(jù)TargetStructMetadata,找到TargetStructMetadata定義中的TargetStructDescriptor,點(diǎn)擊TargetStructDescriptor,找到TargetStructDescriptor的定義
class TargetStructDescriptor final
    : public TargetValueTypeDescriptor<Runtime>,
      public TrailingGenericContextObjects<TargetStructDescriptor<Runtime>,
                            TargetTypeGenericContextDescriptorHeader,
                            /*additional trailing objects*/
                            TargetForeignMetadataInitialization<Runtime>,
                            TargetSingletonMetadataInitialization<Runtime>,
                            TargetCanonicalSpecializedMetadatasListCount<Runtime>,
                            TargetCanonicalSpecializedMetadatasListEntry<Runtime>,
                            TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>> {

public:
 
  uint32_t NumFields;
  
  uint32_t FieldOffsetVectorOffset;
  

};

TargetStructDescriptor.png

TargetStructDescriptor2.png

我們可以發(fā)現(xiàn)TargetStructDescriptor有兩個(gè)屬性,分別是NumFieldsFieldOffsetVectorOffset。
NumFields主要表示結(jié)構(gòu)體中屬性的個(gè)數(shù),如果只有一個(gè)字段偏移量則表示偏移量的長(zhǎng)度。
FieldOffsetVectorOffset表示這個(gè)結(jié)構(gòu)體元數(shù)據(jù)中存儲(chǔ)的屬性的字段偏移向量的偏移量,如果是0則表示沒有。
TargetStructDescriptor 繼承自 TargetValueTypeDescriptor

  • 點(diǎn)擊TargetValueTypeDescriptor,找到TargetValueTypeDescriptor的定義
class TargetValueTypeDescriptor
    : public TargetTypeContextDescriptor<Runtime> {
public:
  static bool classof(const TargetContextDescriptor<Runtime> *cd) {
    return cd->getKind() == ContextDescriptorKind::Struct ||
           cd->getKind() == ContextDescriptorKind::Enum;
  }
};

TargetValueTypeDescriptor.png

我們并沒有找到太多有用的信息,那么我們繼續(xù)向父類尋找。

  • 點(diǎn)擊TargetTypeContextDescriptor,找到TargetTypeContextDescriptor的定義
class TargetTypeContextDescriptor
    : public TargetContextDescriptor<Runtime> {
public:
  
  TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name;

  TargetRelativeDirectPointer<Runtime, MetadataResponse(...),
                              /*Nullable*/ true> AccessFunctionPtr;
  
  TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
                              /*nullable*/ true> Fields;
 
};

TargetTypeContextDescriptor.png

我們發(fā)現(xiàn)TargetTypeContextDescriptorName、AccessFunctionPtr、Fields三個(gè)屬性:
Name就是類型的名稱
AccessFunctionPtr是該類型元數(shù)據(jù)訪問函數(shù)的指針
Fields 是一個(gè)指向該類型的字段描述符的指針
接下來(lái)我們?cè)倏纯?strong>TargetTypeContextDescriptor的父類中還有什么有用的信息

  • 點(diǎn)擊TargetContextDescriptor,找到TargetContextDescriptor的定義
struct TargetContextDescriptor {
  
  ContextDescriptorFlags Flags;
  
  TargetRelativeContextPointer<Runtime> Parent;
};

TargetContextDescriptor.png

這里我們可以看到:
1、這就是descriptor的基類
2、有兩個(gè)屬性,分別是FlagsParent
3、Flags是描述上下文的標(biāo)志,包括它的種類和格式版本。
4、Parent是記錄父類上下文的,如果是頂級(jí)則為null

3.3 Description中的屬性

  • Flags
    Flags.png

    從截圖的源碼中我們可以看到這個(gè)FLags實(shí)際是個(gè)uint32_t的值,按位存儲(chǔ)著kind、isGeneric、isUnique、version、kindSpecificFlags等信息。
  • Parent
    Parent的類型是TargetRelativeContextPointer<Runtime>,我們來(lái)看一下TargetRelativeContextPointer,點(diǎn)擊跳轉(zhuǎn)過(guò)去:
    TargetRelativeContextPointer.png

    我們可以看到TargetRelativeContextPointer是取自RelativeIndirectablePointer的別名,繼續(xù)點(diǎn)擊進(jìn)行查看:
    RelativeIndirectablePointer.png

    根據(jù)注釋知道這個(gè)類的主要作用是存儲(chǔ)在內(nèi)存中的對(duì)象的相對(duì)引用。通過(guò)RelativeOffsetPlusIndirect屬性存儲(chǔ)相對(duì)的地址偏移量。
    再通過(guò)get()函數(shù)獲取,在get()函數(shù)中,會(huì)調(diào)用applyRelativeOffset函數(shù),進(jìn)行地址的偏移。
    applyRelativeOffset.png

applyRelativeOffset2.png

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

  • Fields
  TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
                              /*nullable*/ true> Fields;

這里看看FieldDescriptor點(diǎn)擊跳轉(zhuǎn)到其源碼處,部分源碼如下:

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

這里有5個(gè)屬性:

1、MangledTypeName

2、Superclass

3、kind

4、FieldRecordSize

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

FieldRecord.png

FieldRecord主要是封裝了一些屬性,用于存儲(chǔ)這些值。

3.4 type

我們來(lái)看一下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; });
}

比如說(shuō)這是個(gè)結(jié)構(gòu)體,此時(shí)的impl就是個(gè)StructImpl類型,所以這里的type是StructImpl父類ReflectionMirrorImpl的屬性type。

3.5 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)體為例,此時(shí)的implStructImpl,內(nèi)部的count()函數(shù)

structImpl.png

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

3.6 屬性名和屬性值

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

WX20220119-211231@2x.png

在分析internalReflecting函數(shù)的時(shí)候,我們說(shuō)children是懶加載的,而加載的時(shí)候會(huì)調(diào)用getChild方法,getChild方法源碼入下:
getChild.png

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

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

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)用了implsubscript函數(shù),同樣以結(jié)構(gòu)體為例,我們?cè)?strong>StructImpl中找到該函數(shù),源碼如下:

childMetadata.png

通過(guò)subscript函數(shù)我們可以看到這里面還會(huì)調(diào)用childMetadata獲取到fieldInfo,其實(shí)這里就是獲取type,也就是屬性名,通過(guò)childOffset函數(shù)和index獲取到對(duì)應(yīng)的偏移量,最后根據(jù)內(nèi)存偏移取到屬性值。childMetadata核心點(diǎn)是調(diào)用getFieldAt函數(shù)獲取屬性名稱。

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 result = 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.
  TypeInfo typeInfo;
  if (result.isError()) {
    typeInfo = TypeInfo({&METADATA_SYM(EMPTY_TUPLE_MANGLING),
                         MetadataState::Complete}, {});

    auto *error = result.getError();
    char *str = error->copyErrorString();
    missing_reflection_metadata_warning(
        "warning: the Swift runtime was unable to demangle the type "
        "of field '%*s'. the mangled type name is '%*s': %s. this field will "
        "show up as an empty tuple in Mirrors\n",
        (int)name.size(), name.data(), (int)typeName.size(), typeName.data(),
        str);
    error->freeErrorString(str);
  } else {
    typeInfo = result.getType();
  }

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

我們可以看到在上面這個(gè)方法中:

  • 首先通過(guò)getTypeContextDescriptor獲取baseDesc,也就是我們說(shuō)的Description
  • 然后通過(guò)Fields.get()獲取到fields
  • 接著通過(guò)getFields()[index]獲取對(duì)應(yīng)的field
  • 最后通過(guò)getFieldName()函數(shù)獲取到屬性名稱
  • getTypeContextDescriptor函數(shù)在struct TargetMetadata中,通過(guò)這個(gè)函數(shù)獲取到一個(gè)TargetStructDescriptor,它的父類的父類TargetTypeContextDescriptor中的Fields屬性
  • Fields屬性的類型TargetRelativeDirectPointer中有get方法
  • 實(shí)際中使用的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ù)的空間,在begin和end中:

  • begin就是getFieldRecordBuffer
  • getFieldRecordBuffer就是Begin + NumFields
  • 所以這就是一塊連續(xù)內(nèi)存的訪問
    childOffset 源碼
    分析完了屬性名的獲取,我們來(lái)看看偏移量的獲取。
intptr_t childOffset(intptr_t i) override {
    auto *Clas = static_cast<const ClassMetadata*>(type);
    auto description = Clas->getDescription();

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

    // FIXME: If the class has ObjC heritage, get the field offset using the ObjC
    // metadata, because we don't update the field offsets in the face of
    // resilient base classes.
    uintptr_t fieldOffset;
    if (usesNativeSwiftReferenceCounting(Clas)) {
      fieldOffset = Clas->getFieldOffsets()[i];
    } else {
  #if SWIFT_OBJC_INTEROP
      Ivar *ivars = class_copyIvarList(
          reinterpret_cast<Class>(const_cast<ClassMetadata *>(Clas)), nullptr);
      fieldOffset = ivar_getOffset(ivars[i]);
      free(ivars);
  #else
      swift::crash("Object appears to be Objective-C, but no runtime.");
  #endif
    }
    return (intptr_t)fieldOffset;
  }

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

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)一是通過(guò)獲取Description中的屬性,這里使用的屬性是FieldOffsetVectorOffset。獲取到偏移值后通過(guò)內(nèi)存偏移即可獲取到屬性值。

四、還原StructMetadata

4.1 TargetStructMetadata

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

struct TargetStructMetadata {
    var Kind: Int
    var typeDescription: UnsafeMutablePointer<TargetStructDescriptor>
}

4.2 TargetStructDescriptor

對(duì)于結(jié)構(gòu)體來(lái)說(shuō)其內(nèi)部有7個(gè)屬性
1、flag是個(gè)32位的整形,我們用Int32代替
2、parent是記錄父類的,類型TargetRelativeDirectPointer<Runtime>,這里也可以用Int32代替
3、name是記錄類型的,它的類型是TargetRelativeDirectPointer<char>,所以我們需要實(shí)現(xiàn)一個(gè)TargetRelativeDirectPointer
4、AccessFunctionPtrname類似,內(nèi)部是個(gè)指針
5、Fields也與name類似,內(nèi)部是個(gè)FieldDescriptor
6、NumFields使用Int32
7、FieldOffsetVectorOffset也是用Int32
仿寫實(shí)現(xiàn)如下:

struct TargetStructDescriptor {
    var Flags: Int32
    var Parent: Int32
    var Name: TargetRelativeDirectPointer<CChar>
    var AccessFunctionPtr: TargetRelativeDirectPointer<UnsafeRawPointer>
    var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
    var NumFields: Int32
    /// 每一個(gè)屬性距離當(dāng)前實(shí)例對(duì)象地址的偏移量
    var FieldOffsetVectorOffset: Int32
        
    var genericArgumentOffset: Int {
        return 2
    }
}

4.3 TargetRelativeDirectPointer

  • TargetRelativeDirectPointerRelativeDirectPointer的別名,其內(nèi)部有一個(gè)繼承的RelativeOffset屬性,是int32_t類型,我們可以用Int32代替。
  • 還有一個(gè)get方法,內(nèi)部通過(guò)指針偏移獲取值。
struct TargetRelativeDirectPointer<Pointee> {
    var offset: Int32
    ///模擬RelativeDirectPointerImpl類中的get方法 this+offset指針
    mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
        let offset = self.offset
        return withUnsafePointer(to: &self) { p in
            /*
             獲得self,變?yōu)閞aw,然后+offset
             
             - UnsafeRawPointer(p) 表示this
             - advanced(by: numericCast(offset) 表示移動(dòng)的步長(zhǎng),即offset
             - assumingMemoryBound(to: T.self) 表示假定類型是T,即自己指定的類型
             - UnsafeMutablePointer(mutating:) 表示返回的指針類型
            */
            return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))
        }
    }
}

4.4 FieldDescriptor

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

  • MangledTypeNameRelativeDirectPointer<const char>類型,我們使用TargetRelativeDirectPointer<CChar>代替
  • SuperclassMangledTypeName一樣
  • KindFieldDescriptorKind類型,實(shí)際是uint16_t,這里我們使用UInt16代替
  • FieldRecordSizeUInt16也使用使用UInt16代替
  • NumFields使用Int32代替
  • fields,其實(shí)從屬性上看不到有這個(gè),但是這里面有個(gè)getFieldRecordBuffer方法,通過(guò)this+1的方式一個(gè)一個(gè)的訪問屬性,所以這是一塊連續(xù)的內(nèi)存空間,我們使用fields代替
struct FieldDescriptor {
    var MangledTypeName: TargetRelativeDirectPointer<CChar>
    var Superclass: TargetRelativeDirectPointer<CChar>
    var Kind: UInt16
    var FieldRecordSize:UInt16
    var NumFields: UInt32
    var fields: FiledRecordBuffer<FieldRecord>
}

4.5 FieldRecord

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

  • FlagsFieldRecordFlags類型實(shí)際是uint32_t,這里我們使用Int32代替
  • MangledTypeName使用TargetRelativeDirectPointer<CChar>代替
  • FieldName使用TargetRelativeDirectPointer<CChar>代替
struct FieldRecord {
    var Flags: UInt32
    var MangledTypeName: TargetRelativeDirectPointer<CChar>
    var FieldName: TargetRelativeDirectPointer<CChar>
}

struct FiledRecordBuffer<Element> {
    var element: Element
    
    mutating func buffer(n: Int) -> UnsafeBufferPointer<Element> {
        return withUnsafePointer(to: &self) {
            let ptr = $0.withMemoryRebound(to: Element.self, capacity: 1) { start in
                return start
            }
            return UnsafeBufferPointer(start: ptr, count: n)
        }
    }
    
    mutating func index(of i: Int) -> UnsafeMutablePointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self).advanced(by: i))
        }
    }
}

五、打印屬性信息和屬性值

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

struct ZGPerson {
    var age: Int = 18
    var name: String = "Zhang"
}

var p = ZGPerson()

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

使用unsafeBitCast按位強(qiáng)轉(zhuǎn),將ZGPerson綁定到StructMetadata上,注意,這個(gè)操作非常危險(xiǎn),沒有任何校驗(yàn)和修飾。

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

5.2 打印類名和屬性個(gè)數(shù)

let typeDescription = ptr.pointee.typeDescription

let namePtr = typeDescription.pointee.Name.getmeasureRelativeOffset()
print("current class name: \(String(cString: namePtr))")

let numFields = typeDescription.pointee.NumFields
print("當(dāng)前類屬性的個(gè)數(shù):\(numFields)")

5.3 打印屬性名稱和屬性值

1、打印一下屬性的名稱,首先是獲取到FieldDescriptor的指針,然后通過(guò)內(nèi)存偏移的方式訪問每一個(gè)FieldRecord,最后再訪問FieldRecord中的屬性名。
2、打印屬性值:

  • 首先獲取FieldOffsetVectorOffset的值
  • 然后再加上this也就是當(dāng)前Metadata的指針
  • 這里我們將仿寫的StructMetadata的指針ptr重綁定為Int
  • 源碼中加上FieldOffsetVectorOffset,這里我們就移動(dòng)FieldOffsetVectorOffset
  • 然后將上述移動(dòng)后的綁定為一個(gè)Int32的指針
  • 最后使用UnsafeBufferPointer屬性個(gè)數(shù)創(chuàng)建一個(gè)buffer數(shù)組指針
  • 接下來(lái)我們就可以從數(shù)組中取出每個(gè)屬性的偏移值
  • 然后取出結(jié)構(gòu)體實(shí)例p的內(nèi)存地址
  • 然后按照buffer數(shù)組中的偏移值進(jìn)行偏移,重綁定為屬性的類型
  • 最后就可以打印出屬性值
var bufferPtr = UnsafeBufferPointer(start: UnsafeRawPointer(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self).advanced(by: numericCast(ptr.pointee.typeDescription.pointee.FieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self), count: Int(ptr.pointee.typeDescription.pointee.NumFields))

for i in 0 ..< numFields {
    let fieldDespritor = typeDescription.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.FieldName.getmeasureRelativeOffset()
    print("--- fixed \(String(cString: fieldDespritor)) info begin ---")

    let mangledTypeName = typeDescription.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.MangledTypeName.getmeasureRelativeOffset()

    let genericVector = UnsafeRawPointer(ptr).advanced(by: typeDescription.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)

    let fieldType = swift_getTypeByMangledNameInContext(mangledTypeName, 256, UnsafeRawPointer(typeDescription), UnsafeRawPointer(genericVector).assumingMemoryBound(to: Optional<UnsafeRawPointer>.self))

    let type = unsafeBitCast(fieldType, to: Any.Type.self)
    let value = customCast(type: type)
    let fieldOffset = bufferPtr[Int(i)]
    let valuePtr = withUnsafeMutablePointer(to: &p) { $0 }
    print("fieldType: \(type) \nfieldValue: \(value.get(from: UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(fieldOffset)))))")
    print("--- field: \(String(cString: fieldDespritor)) info end ---\n")
}

本文部分資料參考了下列文獻(xiàn)
作者:憤怒的apple
鏈接:https://juejin.cn/post/7052644744693809183/
來(lái)源:稀土掘金

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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