Swift -- 6.Mirror源碼解析

一.元類型、.self和Self

1.AnyObject

AnyObject代表任意類的實例,類的類型,僅類遵循的協議。

//3.僅類遵循的協議
protocol Myprotocol: AnyObject {
    func test()
}

class LGTeacher {
    var age = 18
    var name = "Kody"
    
}

let t = LGTeacher()
//1.代表實例
var t1: AnyObject = t
//2.代表類的類型
var t2: AnyObject = LGTeacher.self

2.T.self

T.self如果T是實例對象,T.self返回的就是實例本身。如果T是類,返回的就是元類型

//實例對象
let t = LGTeacher() //<LGTeacher: 0x101334100>
//實例對象
let t1 = t.self //<LGTeacher: 0x101334100>
//元類型(metadata)
let t2 = LGTeacher.self //(@thick swiftTest.LGTeacher.Type) t2 = swiftTest.LGTeacher

匯編探究swiftTest.LGTeacher

LGTeacher元類型
  • 此時的rax其實就是元類型,也就是LGTeachermetadata

3.self在不同方法里的表現

class LGTeacher {
    var age = 18
    var name = "Kody"
    
    
    func test() {
        //當前實例對象
        print(self)
    }
    
    static func test1() {
        //self是LGTeacher這個類型本身,也就是metadata
        print(self)
    }
}

4.Self

//2.遵循協議的類型
protocol Myprotocol: AnyObject {
    func get() -> Self
}

class LGTeacher: Myprotocol {
    
    static let age = 18
    //3.訪問類型屬性(Swift5.5已經不能在存儲屬性上使用Self),但是在函數內訪問類型屬性是可以的(第4點)。
//    let age1 = Self.age
    
    //1.作為方法的返回類型
    func test() -> Self {
        return self
    }
    
    func get() -> Self {
        return self
    }
    
    static func getAge() -> Int {
        //4.訪問類型屬性
        return Self.age
    }
    
    func getAge() -> Int {
        //4.訪問類型屬性
        return Self.age
    }
}

5.Any&AnyClass

Any表示任意類型,包括function類型或者Optional類型(Swift里面函數/可選值也是有metadata的)
AnyClass代表任意實例的類型。AnyObject.Type

class LGTeacher {
    var age = 18
}

//AnyClass: AnyObject.Type(任意實例的類型)
//LGTeacher.self(屬于LGTeacher.Type)
//而LGTeacher又屬于AnyObject,因此這樣的寫法是沒有問題的
var t: AnyClass = LGTeacher.self

//這樣寫是不行的,LGTeacher.Type表示LGTeacher.Type.Type,而AnyClass為LGTeacher.Type
//var t1: AnyClass = LGTeacher.Type

6.type(of: T)

獲取一個值的動態類型

var age = 10

//Value的靜態類型:Any
func test(_ value: Any) {
    print(type(of: value)) // Int
}

//self --> type(of:T)
//在我們使用self時,相當于就是type(of:)獲取實際類型
test(age)

例子

protocol Myprotocol: AnyObject {
    func get() -> Self
}
class LGTeacher: Myprotocol {
    var age = 18
    
    //typeof(:self)
    func get() -> Self {
        return self
    }
}

//靜態類型為Myprotocol
let t: Myprotocol = LGTeacher()
//打印的是實際類型
print(t.get()) //swiftTest.LGTeacher

7.探究一個在stackoverflow上的一個問題

問題鏈接

protocol P {
    init()
}

extension P {
    static func createWithBigSelf() -> Self {
        return Self()
    }
    static func createWithLittleSelf() -> Self {
        return self.init()
    }
}

class A : P {
    required init() {}
}

class B : A {}


let x: A.Type = B.self

print(x.createWithBigSelf()) // A
print(x.createWithLittleSelf()) // B

/*
 第一點,要想在函數內使用Self(),必須要實現required init函數。
 
 執行createWithBigSelf時,相當于執行了x的靜態類型傳入了函數內,Self()中的Self指的是當前類型,
 也就是通過傳入的類型創建了一個當前實例,因此x.createWithBigSelf()為A
 
 執行createWithLittleSelf時,執行到self.init()時,其實相當于執行了type(of:T),獲取到了真實類型去構造了一個實例,
 因此x.createWithLittleSelf()為B
 
 當然,這里只是個人角度上分析,也可以從SIL或IR的角度上分析可能更直觀。
 */

二.Swift Runtime

我們在Swift -- 2.類與結構體(下)探究影響函數的派發方式也說了一些關于在Swift上使用Runtime功能,必須加上@objc dynamic
@objc暴露給Runtime Api。消息不會變為消息調度機制objc_msgSend
dynamic賦予動態性
繼承NSObject的類才能在OC中使用

  • 對于純Swift類來說,方法和屬性不加任何修飾符的情況下。這個時候其實已經不具備我們所謂的Runtime特性了
  • 對于純Swift類來說,方法和屬性添加@objc,我們當前可以通過Runtime Api拿到,但是沒法進行調度,因為并沒有賦予它動態性
  • 對于純Swift類來說,沒有動態性,可以添加dynamic修飾,可獲得動態性
  • 對于純Swift類來說,方法和屬性添加@objc dynamic,可以使用Runtime Api進行調度。但是OC代碼中不能使用,因為沒有繼承自NSObject

  • 繼承自NSObjectSwift類,如果想要動態獲取當前的屬性和方法,必須在其聲明之前添加@objc關鍵字,否則也是沒有辦法通過Runtime Api獲取的
  • 繼承自NSObjectSwift類,其繼承自父類的方法具有動態性,其它自定義方法、屬性想要獲得動態性,需要添加dynamic修飾

  • 若方法的參數、屬性類型為Swift特有、無法映射到Objective-C的類型(如果Character、Tuple),則此方法、屬性無法添加dynamic修飾(編譯器報錯)

總結:簡單的來說,如果對于Swift來說如果想使用Runtime Api必須加上@objc,如果想要動態性加上dynamic,如果想要在OC上使用加上NSObject

三.Mirror

所謂反射就是可以動態獲取類型、成員信息,在運行時可以調用方法、屬性等行為的特性。在使用OC開發時很少強調其反射概念,因為OC的Runtime要比其它語言中的反射強大得多。但是Swift是一門類型安全的語言,不支持我們像OC那樣直接操作,它的標準庫依然提供了反射機制來讓我們訪問成員信息。

Swift的反射機制是基于一個叫Mirror的結構體實現的。你為具體的實例創建一個Mirror對象,然后可以通過它查詢這個實例

1.基本用法

class LGTeacher {
    var age = 18
    
    func teach() {
        print("teach")
    }
}

//構建一個Mirror實例,傳入的參數為Any類型
let mirror = Mirror(reflecting: LGTeacher())

/*
 為什么傳入LGTeacher.self,mirror.children是一個空集合?
 因為傳入進去的是一個類型(傳入進去的是metadata),對于元類型來說是沒有屬性和方法,因此也不會反射出數據
 
 */

//注意:當前通過Mirror反射是沒法反射到函數的
for pro in mirror.children {
    //pro.label,當前的名稱
    //pro.value,反射的值
    print("\(pro.label):\(pro.value)") // Optional("age"):18
}

//拿到類名字符串
let className = Mirror(reflecting: LGTeacher()).description.replacingOccurrences(of: "Mirror for", with: "").trimmingCharacters(in: CharacterSet.whitespaces)

2.小案例:將映射中的屬性存放字典中

class LGTeacher {
    var age = 18
}

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


let result = testMirror(LGTeacher())
print(result) //["age": 18]

那么如果我們想讓所有的類都可以實現這個功能,此時可以用protocol協議實現

class LGTeacher {
    var age = 18
}

protocol JsonMap {
    func testMirror() -> Any
}

extension JsonMap {
    func testMirror() -> Any {
        let mirror = Mirror(reflecting: self)
        guard !mirror.children.isEmpty else {
            return self
        }
        var dic = [String:Any]()
        for child in mirror.children {
            if let value = child.value as? JsonMap {
                if let key = child.label {
                    dic[key] = value.testMirror()
                }else {
                    print("No Key")
                }
            }else {
                print("child.value not comfirm JsonMap Protocol")
            }
        }
        return dic
    }
}

extension LGTeacher: JsonMap{}
extension Int: JsonMap {}

let result = LGTeacher().testMirror()
print(result) //["age": 18]

添加Error優化當前代碼的錯誤信息

class LGTeacher {
    var age = 18
}

enum JsonMapError: Error {
    case emptyKey
    case notComfirmProtocol
}

protocol JsonMap {
    func testMirror() throws -> Any
}

extension JsonMap {
    func testMirror() throws -> Any {
        let mirror = Mirror(reflecting: self)
        guard !mirror.children.isEmpty else {
            return self
        }
        var dic = [String:Any]()
        for child in mirror.children {
            if let value = child.value as? JsonMap {
                if let key = child.label {
                    dic[key] = try value.testMirror()
                }else {
                    throw JsonMapError.emptyKey
                }
            }else {
                throw JsonMapError.notComfirmProtocol
            }
        }
        return dic
    }
}

extension LGTeacher: JsonMap{}
extension Int: JsonMap {}

let result = try? LGTeacher().testMirror()
print(result) //Optional(["age": 18])

關于rethrows

rethrows本身并不拋出異常或處理異常,只是起到了傳遞異常的作用。通常在閉包中使用

enum ClosureError: Error {
    case lessZero
}

func test(closure: (Int) throws -> Int, num: Int) rethrows -> Int {
    try closure(num)
}

do {
    let result = try test(closure: {
        if $0 < 0 {
            throw ClosureError.lessZero
        }else {
            return $0 + 10
        }
    }, num: 1)
    print(result)
}catch {
    print(error) //lessZero
}

3.Mirror源碼解析

進入源文件搜索Mirror.swift,忽略掉一些細節,在源碼中找到初始化方法

  public init(reflecting subject: Any) {
    if case let customized as CustomReflectable = subject {
      self = customized.customMirror
    } else {
      self = Mirror(internalReflecting: subject)
    }
  }
  • 如果subject遵循了CustomReflectable協議self = customized.customMirror
  • 否則self等于內部的反射初始化函數生成的實例

1.關于CustomReflectable

class LGTeacher: CustomReflectable {
    var age = 18
    var name = "Kody"
    
    var customMirror: Mirror {
        //KeyValuePairs,鍵值對
        let info = KeyValuePairs<String,Any>(dictionaryLiteral: ("currentAge",age), ("currentName", name))
        let mirror = Mirror.init(self, children: info, displayStyle: .class, ancestorRepresentation: .generated)
        return mirror
    }
}

let mirror = Mirror(reflecting: LGTeacher())

for child in mirror.children {
    if let label = child.label {
        print("\(label):\(child.value)")
    }
}

//此時打印的值,就是我們在customMirror中聲明的KeyValuePairs
//currentAge:18
//currentName:Kody

//通過LLDB調試時,也能反射出當前的調試信息
//(lldb) po LGTeacher()
//? <LGTeacher: 0x10b156b70>
//  - currentAge : 18
//  - currentName : "Kody"

2.關于Mirror的內部初始化方法Mirror(internalReflection:subject))

extension Mirror {
  internal init(internalReflecting subject: Any,
              subjectType: Any.Type? = nil,
              customAncestor: Mirror? = nil)
  {
    //獲取傳進來的subject的類型信息。使用type(of: T)獲取真實信息
    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)
    
    ...
  }

關于_getNormalizedType_getChildCount函數

@_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_nameSwift的一個隱藏符號,作用是將某個C/C++語言函數直接映射為Swift函數

3.關于Swift調用C語言函數

傳統思路

1.創建C文件,.h(方法的聲明)和.c(方法的實現)文件
//testC.h
int sum(int a, int b);
//testC.c
int sum(int a, int b) {
    return  a + b;
}

2.創建橋接文件,import我們創建的.h文件
//一般在導入C/C++文件時使用include,導入OC文件使用improt
#include "testC.h"

3.在Swift代碼中使用

使用@_silgen_name

1.第一步不變

2.使用_silgen_name直接將C函數映射成Swift函數
@_silgen_name("sum")
internal func my_sum(a: Int, b: Int) -> Int

let result = my_sum(a: 10, b: 10)
print(result) //20

訪問控制(Access Control)

  • open可以在任意地方被訪問、繼承和重寫
  • public可以在任意地方被訪問,在其它模塊(module)內不能被繼承、重寫
  • internal默認。在整個模塊內可以訪問、繼承和重寫
  • fileprivaty在同一個文件內(.swift)可以被訪問、繼承和重寫。修飾的函數靜態派發
  • privaty私有的。只能在自己的類中訪問。修飾的函數靜態派發

  • final只能修飾class。意味著該類是最終類,不能被繼承。修飾的class中的函數派發方式為靜態派發

4.找到C++代碼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; });
}
  • call調用了一個函數,通過回調函數impl->type,返回type的信息
  • 通過注釋可以得出Any.Type就是Metadata

5.call的實現

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;
  }
  
  //如果不是Class,執行這個call回調函數
  auto call = [&](ReflectionMirrorImpl *impl) {
    impl->type = type;
    impl->value = value;
    auto result = f(impl);
    return result;
  };
  //如果是Class,執行callClass回調函數
  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);
  };
  
  //通過MetadataKind來判斷類型,執行不同的處理
  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);
    }

    ...
}
  • 關于Call函數,聲明了2個回調函數來獲取Class非Class的type
  • 這2個回調函數都有一個參數ReflectionMirrorImpl
  • 根據不同的MetadataKind有不同的Impl,例如枚舉EnumImpl

6.關于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反射實現的抽象基類

7.關于EnumImpl

// Implementation for enums.
struct EnumImpl : ReflectionMirrorImpl {
  //是否能反射
  bool isReflectable() {
    //做一個metadata的強轉
    const auto *Enum = static_cast<const EnumMetadata *>(type);
    //找到metadata的Description
    const auto &Description = Enum->getDescription();
    //根據Description中的isReflectable字段來判斷是否可以反射
    return Description->isReflectable();
  }
  
  const char *getInfo(unsigned *tagPtr = nullptr,
                      const Metadata **payloadTypePtr = nullptr,
                      bool *indirectPtr = nullptr) {
    // 'tag' is in the range [0..NumElements-1].
    unsigned tag = type->vw_getEnumTag(value);

    StringRef name;
    FieldType info;
    //獲取FieldDescriptor的信息,也就是屬性信息存放的地方
    std::tie(name, info) = getFieldAt(type, tag);
    const Metadata *payloadType = info.getType();
    bool indirect = info.isIndirect();

    if (tagPtr)
      *tagPtr = tag;
    if (payloadTypePtr)
      *payloadTypePtr = payloadType;
    if (indirectPtr)
      *indirectPtr = indirect;
    
    return name.data();
  }

  char displayStyle() override {
    return 'e';
  }
  
  //獲取count
  intptr_t count() override {
    if (!isReflectable()) {
      return 0;
    }
    
    // No fields if reflecting the enumeration type instead of a case
    if (!value) {
      return 0;
    }

    const Metadata *payloadType;
    //獲取掛載類型,也就是Metadata
    getInfo(nullptr, &payloadType, nullptr);
    return (payloadType != nullptr) ? 1 : 0;
  }

  ...
}

8.關于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))};
  };
  //獲取TargetxxxDescriptor信息
  auto *baseDesc = base->getTypeContextDescriptor();
  if (!baseDesc)
    return failedToFindMetadata();

  //獲取descriptor里的FieldDescriptor的信息
  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();
  ...
}
  • 此時的邏輯和Swift -- 3.屬性通過Mach-O找到我們的屬性名稱是一致的

Mirror的工作原理:可以看出Mirro是通過Metadata(當前類型的元數據)getDescription(當前類型的描述)FieldDescription(當前類型屬性的描述)來實現的。

四.結合源碼還原TargetEnumMetaData

Swift -- 1.類與結構體(上)探究了TargetClassMetadata的數據類型,在尋找的過程中也遇到了TargetEnumMetaData,因此結合源碼還原一下TargetEnumMetaData

1.關于MetadataKind

之前我們總結了一張關于MetadataKind的表

name value
Class 0x0
Struct 0x200
Enum 0x201
Optional 0x202
ForeignClass 0x203
Opaque 0x300
Tuple 0x301
Function 0x302
Existential 0x303
Metatype 0x304
ObjCClassWrapper 0x305
ExistentialMetatype 0x306
HeapLocalVariable 0x400
HeapGenericLocalVariable 0x500
ErrorObject 0x501
LastEnumerated 0x7FF

那么這些值是怎么的來的呢?接下來通過源碼分析一下

  1. kind的內存大小
//1.在TargetMetaData中找到kind
/// The kind. Only valid for non-class metadata; getKind() must be used to get
/// the kind value.
StoredPointer Kind;

//2.關于StoredPointer
using StoredPointer = typename Runtime::StoredPointer;

//3.關于Runtime
using Runtime = External<RuntimeTarget<sizeof(uintptr_t)>>;

//4.關于uintptr_t
typedef unsigned long           uintptr_t;
  • 得出kind大小為Int64

2.驗證kind

找到MetadataKind

/// Non-type metadata kinds have this bit set.
//沒有類型的元數據的kind存放在此位
const unsigned MetadataKindIsNonType = 0x400;

/// Non-heap metadata kinds have this bit set.
//不是堆元數據存放在此位。也就是除了引用類型
const unsigned MetadataKindIsNonHeap = 0x200;

//運行時私有元數據已設置此位
const unsigned MetadataKindIsRuntimePrivate = 0x100;

/// Kinds of Swift metadata records.  Some of these are types, some
/// aren't.
enum class MetadataKind : uint32_t {
#define METADATAKIND(name, value) name = value,
#define ABSTRACTMETADATAKIND(name, start, end)                                 \
  name##_Start = start, name##_End = end,
#include "MetadataKind.def"
  
  /// The largest possible non-isa-pointer metadata kind value.
  ///
  /// This is included in the enumeration to prevent against attempts to
  /// exhaustively match metadata kinds. Future Swift runtimes or compilers
  /// may introduce new metadata kinds, so for forward compatibility, the
  /// runtime must tolerate metadata with unknown kinds.
  /// This specific value is not mapped to a valid metadata kind at this time,
  /// however.
  LastEnumerated = 0x7FF,
};

//關于MetadataKind.def

/// A class type.
NOMINALTYPEMETADATAKIND(Class, 0)

/// A struct type.
NOMINALTYPEMETADATAKIND(Struct, 0 | MetadataKindIsNonHeap)

/// An enum type.
/// If we add reference enums, that needs to go here.
NOMINALTYPEMETADATAKIND(Enum, 1 | MetadataKindIsNonHeap)

/// An optional type.
NOMINALTYPEMETADATAKIND(Optional, 2 | MetadataKindIsNonHeap)

/// A foreign class, such as a Core Foundation class.
METADATAKIND(ForeignClass, 3 | MetadataKindIsNonHeap)

/// A type whose value is not exposed in the metadata system.
METADATAKIND(Opaque, 0 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)

/// A tuple.
METADATAKIND(Tuple, 1 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)

/// A monomorphic function.
METADATAKIND(Function, 2 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)

/// An existential type.
METADATAKIND(Existential, 3 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)

/// A metatype.
METADATAKIND(Metatype, 4 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)

/// An ObjC class wrapper.
METADATAKIND(ObjCClassWrapper, 5 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)

/// An existential metatype.
METADATAKIND(ExistentialMetatype, 6 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)

/// A heap-allocated local variable using statically-generated metadata.
METADATAKIND(HeapLocalVariable, 0 | MetadataKindIsNonType)

/// A heap-allocated local variable using runtime-instantiated metadata.
METADATAKIND(HeapGenericLocalVariable,
             0 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)

/// A native error object.
METADATAKIND(ErrorObject,
             1 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)

/// A heap-allocated task.
METADATAKIND(Task,
             2 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)

/// A non-task async job.
METADATAKIND(Job,
             3 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)

2.通過源碼還原TargetEnumMetadata

1.進入TargetEnumMetadata,里面并沒有結構信息

2.進入TargetEnumMetadata父類TargetValueMetadata

  /// An out-of-line description of the type.
  TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;
  • 存在一個Description描述信息

3.進入TargetValueMetadata父類TargetMetadata(最終基類)

StoredPointer Kind;

4.此時我們就能確定TargetEnumMetadata的外層結構結構

struct TargetEnumMetadata {
    var kind: Int
    var descriptor: UnsafePointer<TargetEnumDescriptor>
}

struct TargetEnumDescriptor {
    
}

5.進入TargetEnumDescriptor

  /// The number of non-empty cases in the enum are in the low 24 bits;
  /// the offset of the payload size in the metadata record in words,
  /// if any, is stored in the high 8 bits.
  uint32_t NumPayloadCasesAndPayloadSizeOffset;

  /// The number of empty cases in the enum.
  uint32_t NumEmptyCases;
  • NumPayloadCasesAndPayloadSizeOffset具有掛載的case數量
  • NumEmptyCases沒有掛載的case數量

6.進入TargetEnumDescriptor父類TargetValueTypeDescriptor

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;

  ...
}
  • 存有NameAccessFunctionPtrFields3個變量,并且類型都是TargetRelativeDirectPointer

7.進入TargetTypeContextDescriptor父類TargetContextDescriptor(最終基類)

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

  ...
}

using RelativeContextPointerIntPair =
  RelativeIndirectablePointerIntPair<const Context<Runtime>, IntTy,
                              /*nullable*/ true, int32_t,
                              TargetSignedContextPointer<Runtime, Context>>;
  • Flags大小為UInt32
  • Parent類型為TargetRelativeContextPointer

8.總結一下TargetEnumMetadata數據結構

TargetEnumMetadata數據結構

9.關于TargetRelativeDirectPointerTargetRelativeContextPointer

相對地址的指針

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

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

/// 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;
};
  • 類似于我們在Mach-o中尋找屬性時所看到的很多偏移量,在Swift中大量使用相對地址指針

  • 直接尋址,比如我們定義一個var a = 10 ,那么通過a的指針地址0x1000拿到變量10在內存中的地址(比如是0x1004)。此時a通過2步拿到了內存中的10

  • 相對尋址,比如我們定義一個var a = 10 ,那么a指針地址0x1000存放的是offset,這個offset代表著10在內存中的地址(0x1004)與當前a地址的偏移量,也就是4。這樣就可以通過offset來獲取值。節省大量的地址信息,優化內存空間

翻譯為Swift中的相對指針

struct TargetRelativeDirectPointer<Pointee> {
    var offset: Int32
    
    mutating func getMeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
        let offset = self.offset
        return withUnsafePointer(to: &self) { p in
            return UnsafeMutableRawPointer(mutating: p.advanced(by: numericCast(offset))).assumingMemoryBound(to: Pointee.self)
        }
    }
}

10.關于mangledTypeName的還原

使用C語言標準庫函數swift_getTypeByMangledNameInContext

//方式1:.h文件聲明
/*
 typeNameStart,混淆名稱的指針地址
 typeNameLength,混淆名稱的長度
 context,上下文,Descriptor的指針地址
 genericArgs,如果有泛型參數,泛型參數的指針地址。參考HandyJson,metadata地址再偏移2個原生指針的大小(2個8字節)
 */
const void * _Nullable swift_getTypeByMangledNameInContext(
                        const void * _Nullable typeNameStart,
                        int typeNameLength,
                        const void * _Nullable context,
                        const void * _Nullable const * _Nullable genericArgs);

//這里使用編譯器字段@_silgen_name()方式
@_silgen_name("swift_getTypeByMangledNameInContext")
private func getTypeByMangledNameInContext(
    mangledNamePtr: UnsafePointer<UInt8>,
    mangledNameLength: Int,
    genericContext: UnsafeRawPointer?,
    genericArgs: UnsafeRawPointer?) -> Any.Type?

func getTypeByMangleName(
    mangledNamePtr: UnsafePointer<UInt8>,
    genericContext: UnsafeRawPointer?, metadata: UnsafeRawPointer) ->  Any.Type? {
        return getTypeByMangledNameInContext(mangledNamePtr: mangledNamePtr,
                                             mangledNameLength: getMangledTypeNameSize(mangledNamePtr),
                                             genericContext: UnsafeRawPointer(genericContext),
                                             genericArgs: getGenericArgs(metadata))
}

//參考HandyJson,暫時沒找到測量mangledName的長度
func getMangledTypeNameSize(_ managedName: UnsafePointer<UInt8>) -> Int {
    // TODO: should find the actually size 
    return 256
}

//獲取泛型參數在metadata中的偏移量
func getGenericArgumentOffset() -> Int {
    return 2
}

//如果有泛型參數,需要傳入的泛型參數指針。參考HandyJson
func getGenericArgs(_ metadata: UnsafeRawPointer) -> UnsafeRawPointer? {
    let offSetPtr = metadata.advanced(by: getGenericArgumentOffset() * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Int.self)
    if offSetPtr.pointee == 0 {
        return nil
    }
    return UnsafeRawPointer(offSetPtr)
}

11.還原TargetEnumMetadata

TangledTypeName.swift

@_silgen_name("swift_getTypeByMangledNameInContext")
private func getTypeByMangledNameInContext(
    mangledNamePtr: UnsafePointer<UInt8>,
    mangledNameLength: Int,
    genericContext: UnsafeRawPointer?,
    genericArgs: UnsafeRawPointer?) -> Any.Type?

func getTypeByMangleName(
    mangledNamePtr: UnsafePointer<UInt8>,
    genericContext: UnsafeRawPointer?) ->  Any.Type? {
        return getTypeByMangledNameInContext(mangledNamePtr: mangledNamePtr, mangledNameLength: getMangledTypeNameSize(mangleTypeNamePtr), genericContext: UnsafeRawPointer(genericContext), genericArgs: getGenericArgs(mangledNamePtr))
}

//參考HandyJson,暫時沒找到測量mangledName的長度
func getMangledTypeNameSize(_ managedName: UnsafePointer<UInt8>) -> Int {
    // TODO: should find the actually size 
    return 256
}

//獲取泛型參數在Descriptor中的偏移量
func getGenericArgumentOffset() -> Int {
    return 2
}

//如果有泛型參數,需要傳入的泛型參數指針
func getGenericArgs(_ descriptor: UnsafeRawPointer) -> UnsafeRawPointer {
    return UnsafeRawPointer(descriptor.advanced(by: getGenericArgumentOffset() * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self))
}

TargetMetadata.swift

struct TargetEnumMetadata{
    var kind: Int
    var descriptor: UnsafeMutablePointer<TargetEnumDescriptor>
}

struct TargetEnumDescriptor {
    var flags: UInt32
    var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
    var name: TargetRelativeDirectPointer<CChar>
    var accessFunctionPtr: TargetRelativeDirectPointer<UnsafeRawPointer>
    var fields: TargetRelativeDirectPointer<FieldDescriptor>

    var numPayloadCaseAndPayloadSizeOffset: UInt32
    var numEmptyCases: UInt32
}

struct FieldDescriptor {
    var mangledTypeName: TargetRelativeDirectPointer<UInt8>
    var superClass: TargetRelativeDirectPointer<CChar>
    var kind: UInt16
    var fieldRecordSize: UInt16
    var numField: UInt32
    
    //連續的內存空間存放我們的var變量/case的名稱
    //UnsafeBufferPointer,連續的內存空間指針
    var fieldRecords: FieldRecordBuffer<FieldRecord>
    
    /*
     kind: FieldDescriptorKind(枚舉值依次遞增)
     Struct: 0x1
     Class
     Enum
     MultiPayloadEnum
     Protocol
     ClassProtocol
     ObjCProtocol
     ObjCClass
     */
}


struct FieldRecordBuffer<Element> {
    var element: Element
    
    //方式1.返回buffer連續內存空間
    mutating func buffer(count: Int) -> UnsafeMutableBufferPointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(start: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self), count: count))
        }
    }
    
    //方式2.根據下標獲取數組元素
    mutating func getFieldRecord(index: Int) -> UnsafeMutablePointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutablePointer(mutating: UnsafeRawPointer($0.advanced(by: index)).assumingMemoryBound(to: Element.self))
        }
    }
}

struct FieldRecord {
    var flags: UInt32
    var mangledTypeName: TargetRelativeDirectPointer<UInt8>
    var fieldName: TargetRelativeDirectPointer<CChar>
    
    /*
     flags: FieldRecordFlags
     IsIndirectCase = 0x1,
     IsVar = 0x2,
     IsArtificial = 0x4,
     其它情況 = 0
     */
}

struct TargetRelativeDirectPointer<Pointee> {
    var offset: Int32

    mutating func getMeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
        let offset = self.offset
        return withUnsafePointer(to: &self) {
            /*
             注意:這里的$0必須轉化為UnsafeRawPointer再執行advanced
             如果類型指針advanced會出問題。
             原始是:在介紹指針的時候說過,如果是類型指針的話,
             advanced(x)相當于移動該類型的內存大小*x。
             */
            return UnsafeMutableRawPointer(mutating: UnsafeRawPointer($0).advanced(by: numericCast(offset))).assumingMemoryBound(to: Pointee.self)
        }
    }
}

main.swift

enum Shape {
    case circle(radious:Int)
    case rectangle(Bool)
    case triangle
}

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

//錯誤寫法
//var descriptor = ptr.pointee.descriptor.pointee
//let namePtr = descriptor.name.getMeasureRelativeOffset()

//這里只能這樣寫,值類型賦值會拷貝。執行到getMeasureRelativeOffset會出問題
let namePtr = ptr.pointee.descriptor.pointee.name.getMeasureRelativeOffset()

print("enum kind: ", "0x\(String(ptr.pointee.kind, radix: 16))") //0x201對應Kind表為Enum
//枚舉的名稱
print("enum name", String(cString: namePtr)) // Shape
//枚舉的帶有掛載的數量
print("enum numPayloadCaseAndPayloadSizeOffset: ", ptr.pointee.descriptor.pointee.numPayloadCaseAndPayloadSizeOffset) // 1
//枚舉的沒有帶掛載的數量
print("enum numEmptyCases: ", ptr.pointee.descriptor.pointee.numEmptyCases) // 2


let fieldPtr = ptr.pointee.descriptor.pointee.fields.getMeasureRelativeOffset()

//1.關于還原混淆后的TypeName
let mangleTypeNamePtr = fieldPtr.pointee.mangledTypeName.getMeasureRelativeOffset()

let nameType = getTypeByMangleName(mangledNamePtr: mangleTypeNamePtr, genericContext: UnsafeRawPointer(ptr.pointee.descriptor), metadata: ptr)

print("type Name", nameType as Any)

//2.superClass
let superClassPtr = fieldPtr.pointee.superClass.getMeasureRelativeOffset()
print("enum superClass: ", String(cString: superClassPtr)) //枚舉并沒有superClass

//3.kind
print("enum field kind: ", fieldPtr.pointee.kind)

//4.fieldRecordSize
print("enum fieldRecordSize: ",fieldPtr.pointee.fieldRecordSize)

//5.numField
print("enum numField: ",fieldPtr.pointee.numField)

//6.FieldRecod

//方式1:使用Buffer的方式
let bufferPtr = fieldPtr.pointee.fieldRecords.buffer(count: numericCast(fieldPtr.pointee.numField))
for i in 0..<bufferPtr.count {
    print("type:\(getTypeByMangleName(mangledNamePtr: bufferPtr[i].mangledTypeName.getMeasureRelativeOffset(), genericContext: ptr.pointee.descriptor, metadata: ptr) as Any) case值:\(String(cString: bufferPtr[i].fieldName.getMeasureRelativeOffset()))")
}


//方式2:使用指針位移的方式
//for i in 0..<fieldPtr.pointee.numField {
//    let filedRecord = fieldPtr.pointee.fieldRecords.getFieldRecord(index: numericCast(i))
//    //這里的flags為0
//
//    print("type:\(getTypeByMangleName(mangledNamePtr: filedRecord.pointee.mangledTypeName.getMeasureRelativeOffset(), genericContext: ptr.pointee.descriptor) as Any) case值:\(String(cString: filedRecord.pointee.fieldName.getMeasureRelativeOffset()))")
//}

打印信息

enum kind:  0x201
enum name Shape
enum numPayloadCaseAndPayloadSizeOffset:  2
enum numEmptyCases:  1
type Name Optional(swiftTest.Shape)
enum superClass:  
enum field kind:  3
enum fieldRecordSize:  12
enum numField:  3
type:Optional((radious: Swift.Int)) case值:circle
type:Optional(Swift.Bool) case值:rectangle
type:nil case值:triangle

五.參考HandyJson總結TargetClassMetadata

Swift -- 4.指針&內存管理通過指針獲取了類名屬性名稱、、vtable及分析了vtable中的函數名稱存放位置(符號表及字符串表)

接下來我們需要獲取到的是屬性類型屬性值

fieldOffsetVectorOffset可以理解為屬性信息基于metadata的偏移量(value*8字節)。連續的內存空間,每8字節存放屬性值內存地址基于metadata的偏移量
可以理解為取出屬性值需要拿到2次的offSet,根據內存地址然后獲取到最終的值

TargetMetadata.swift

struct TargetClassMetadata{
    var kind: Int
    var superClass: Any.Type
    var cacheData: (Int, Int)
    var data: Int
    var classFlags: Int32
    var instanceAddressPoint: UInt32
    var instanceSize: UInt32
    var instanceAlignmentMask: UInt16
    var reserved: UInt16
    var classSize: UInt32
    var classAddressPoint: UInt32
    var typeDescriptor: UnsafeMutablePointer<TargetClassDescriptor>
    var iVarDestroyer: UnsafeRawPointer
}

struct TargetClassDescriptor{
    var flags: UInt32
    var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
    var name: TargetRelativeDirectPointer<CChar>
    var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>
    var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
    var superClassType: TargetRelativeDirectPointer<CChar>
    var metadataNegativeSizeInWords: UInt32
    var metadataPositiveSizeInWords: UInt32
    var numImmediateMembers: UInt32
    var numFields: UInt32
    var fieldOffsetVectorOffset: UInt32
    var Offset: UInt32
    //VTable數量
    var size: UInt32
    //VTable
    var firstVtableData: VTableBuffer<VTable>
}

struct VTableBuffer<Element> {
    var element: Element
    
    mutating func buffer(of count: Int) -> UnsafeMutableBufferPointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(start: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self), count: count))
        }
    }
}

struct VTable {
    var flags: TargetMethodDescriptor
    //注意此時的相對地址指針的偏移量不能使用Int,因為符號位會影響offset
    var methodImp: TargetRelativeDirectPointerOffsetUint<UnsafeRawPointer>
}

struct TargetMethodDescriptor {
    var flagType: UInt32

    //從源碼恢復的
    enum KindType: UInt32 {
        case Method,
        Init,
        Getter,
        Setter,
        ModifyCoroutine,
        ReadCoroutine
    }

    enum FlagTypeMask: UInt32 {
        case  KindMask = 0x0F,                // 16 kinds should be enough for anybody
        IsInstanceMask = 0x10,
        IsDynamicMask = 0x20,
        IsAsyncMask = 0x40,
//        ExtraDiscriminatorShift = 16, //這個16與0x10沖突了
        ExtraDiscriminatorMask = 0xFFFF0000
      }

    func kindType() -> KindType {
        return KindType(rawValue: flagType & FlagTypeMask.KindMask.rawValue)!
    }

}

struct FieldDescriptor {
    var mangledTypeName: TargetRelativeDirectPointer<UInt8>
    var superClass: TargetRelativeDirectPointer<CChar>
    var kind: UInt16
    var fieldRecordSize: UInt16
    var numField: UInt32
    
    //連續的內存空間存放我們的var變量/case的名稱
    //UnsafeBufferPointer,連續的內存空間指針
    var fieldRecords: FieldRecordBuffer<FieldRecord>
    
    /*
     kind: FieldDescriptorKind(枚舉值依次遞增)
     Struct: 0x1
     Class
     Enum
     MultiPayloadEnum
     Protocol
     ClassProtocol
     ObjCProtocol
     ObjCClass
     */
}

struct FieldRecordBuffer<Element> {
    var element: Element
    
    //方式1.返回buffer連續內存空間
    mutating func buffer(count: Int) -> UnsafeMutableBufferPointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(start: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self), count: count))
        }
    }
    
    //方式2.根據下標獲取數組元素
    mutating func getFieldRecord(index: Int) -> UnsafeMutablePointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutablePointer(mutating: UnsafeRawPointer($0.advanced(by: index)).assumingMemoryBound(to: Element.self))
        }
    }
}

struct FieldRecord {
    var flags: UInt32
    var mangledTypeName: TargetRelativeDirectPointer<UInt8>
    var fieldName: TargetRelativeDirectPointer<CChar>
    
    /*
     flags: FieldRecordFlags
     IsIndirectCase = 0x1,
     IsVar = 0x2,
     IsArtificial = 0x4,
     其它情況 = 0
     */
}

struct TargetRelativeDirectPointer<Pointee> {
    var offset: Int32

    mutating func getMeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
        let offset = self.offset
        return withUnsafePointer(to: &self) {
            /*
             注意:這里的$0必須轉化為UnsafeRawPointer再執行advanced
             如果類型指針advanced會出問題。
             原始是:在介紹指針的時候說過,如果是類型指針的話,
             advanced(x)相當于移動該類型的內存大小*x。
             */
            return UnsafeMutableRawPointer(mutating: UnsafeRawPointer($0).advanced(by: numericCast(offset))).assumingMemoryBound(to: Pointee.self)
        }
    }
}

struct TargetRelativeDirectPointerOffsetUint<Pointee> {
    var offset: UInt32

    mutating func getMeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
        let offset = self.offset
        return withUnsafePointer(to: &self) {
            /*
             注意:這里的$0必須轉化為UnsafeRawPointer再執行advanced
             如果類型指針advanced會出問題。
             原始是:在介紹指針的時候說過,如果是類型指針的話,
             advanced(x)相當于移動該類型的內存大小*x。
             */
            return UnsafeMutableRawPointer(mutating: UnsafeRawPointer($0).advanced(by: numericCast(offset))).assumingMemoryBound(to: Pointee.self)
        }
    }
}

main.swift

class LGTeacher {
    var age = 18
    var name = "Kody"
    
    func teach() {
        print("teach")
    }
    
    func teach1() {
        print("teach1")
    }
    
    func teach2() {
        print("teach2")
    }
}

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

let descriptorPtr = ptr.pointee.typeDescriptor

//1.獲取類的名稱
print("Class Name:", String(cString: descriptorPtr.pointee.name.getMeasureRelativeOffset()))

//2.獲取類的類型
let fieldDescriptorPtr = descriptorPtr.pointee.fieldDescriptor.getMeasureRelativeOffset()
print("Class type:", getTypeByMangleName(mangledNamePtr: fieldDescriptorPtr.pointee.mangledTypeName.getMeasureRelativeOffset(), genericContext: descriptorPtr, metadata: ptr) as Any)

//2.獲取屬性名稱

let fieldRecordBuffer = fieldDescriptorPtr.pointee.fieldRecords.buffer(count: numericCast(fieldDescriptorPtr.pointee.numField))

let propertyPtr = UnsafeRawPointer(ptr).advanced(by: numericCast(descriptorPtr.pointee.fieldOffsetVectorOffset))

let t = LGTeacher()
//3.獲取t的metadata地址
let tMetadataPtr = Unmanaged.passUnretained(t).toOpaque()

//4.獲取屬性值的信息
//獲取屬性值(offset)存放的地址(基于metadata偏移fieldOffsetVectorOffset個8字節,每8字節存放1個屬性的偏移信息)
let offsets = UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self).advanced(by: numericCast(descriptorPtr.pointee.fieldOffsetVectorOffset))

protocol AnyExtensions {}

extension AnyExtensions {

    //取數據
    //這里的self指的是當前類型
    static func value(_ pointer: UnsafeRawPointer) -> Any {
        return pointer.assumingMemoryBound(to: self).pointee
    }
}

struct TargetProtocolMetadata {
    var type: Any.Type
    //協議見證表,后續寫到協議相關的再談
    var witness: Int
}

for i in 0..<fieldRecordBuffer.count {
    let type = getTypeByMangleName(mangledNamePtr: fieldRecordBuffer[i].mangledTypeName.getMeasureRelativeOffset(), genericContext: descriptorPtr, metadata: ptr)!
    
    let fieldName = String(cString: fieldRecordBuffer[i].fieldName.getMeasureRelativeOffset())
    
    //獲取屬性偏移信息
    let valueOffset = offsets[i]
    
    /*
     屬性在內存中的實際地址
     其實如果能夠聯想到前面的metadata,
     此時的2個屬性肯定是放在heapObject(16字節)后
     大膽可以猜測一下這里的偏移量一個是16一個是24
     */
    let valuePtr = tMetadataPtr.advanced(by: valueOffset)
    
    //疑問:現在有了type怎么取出不同的值?
    
    /*
     方式1:根據HandyJson源碼,利用協議里self/Self獲取實際類型的特性。
     將聲明的Extensions的metadata更換為我們得知的metadata,利用協議方法內self/Self能夠獲取真實類型的特性,完成取值操作。
     */
    
    //獲取結構體的元數據,靜態類型為AnyExtensions,動態類型為Extensions
    struct Extensions: AnyExtensions {}
    var extensions: AnyExtensions.Type = Extensions.self
    
    //拿到extension的地址更換metadata
    withUnsafePointer(to: &extensions) {
        UnsafeMutableRawPointer(mutating: $0).assumingMemoryBound(to: Any.Type.self).pointee = type
    }
    //取值
//    let value = extensions.value(valuePtr)
//    print("\(fieldName)->\(type)->\(extensions.value(valuePtr))")

    /*
     方式2:將自定義的結構體(已經是新的metadata)按位轉化為Protocol.Type實現對maetadata的更換。
     這里的TargetProtocolMetadata指的是通過源碼總結出的metadata數據結構
     一樣的原理,也是通過更換了metadata來實現不同數據的取值,這里不同的是轉化前metadata就已經是修改后的。
     */
    
    let protocolMetaData = TargetProtocolMetadata(type: type, witness: 0)
    let protocolType = unsafeBitCast(protocolMetaData, to: AnyExtensions.Type.self)
    print("\(fieldName)->\(type)->\(protocolType.value(valuePtr))")
}

//3.獲取V-Table,及執行函數方法
let vtableBuffer = descriptorPtr.pointee.firstVtableData.buffer(of: numericCast(descriptorPtr.pointee.size))

typealias Function = @convention(c) ()-> Void

var vmAddressSize = getsegbyname("__PAGEZERO").pointee.vmsize

for i in 0..<vtableBuffer.count {
    if vtableBuffer[i].flags.kindType() == .Method {
        let impOffsetPtr = vtableBuffer[i].methodImp.getMeasureRelativeOffset()
        //當然,這里的減去虛擬內存基地址邏輯,還可以添加一個func在相對指針內部偏移時實現
        let imp_Decimal = numericCast(Int(bitPattern: impOffsetPtr)) - vmAddressSize
        let impPtr = UnsafeRawPointer(bitPattern: UInt(imp_Decimal))
        let function = unsafeBitCast(impPtr, to: Function.self)
        function()
    }
}

打印信息

Class Name: LGTeacher
Class type: Optional(swiftTest.LGTeacher)
age->Int->18
name->String->Kody
teach
teach1
teach2

六.還原TargetStructMetadata

還原結構體名稱屬性名稱屬性類型屬性值

查看源碼路線和TargetEnumMetadata一致,這里由于篇幅問題就不一一還原了。大概邏輯可以看看TargetEnumMetadata

TargetStructMetadata數據結構
  • 可以看出基本上與值類型的TargetEnumMetadata共用一套邏輯

  • Struct是結構體,它的內存地址就是指向的第一個元素。因此它的第一個屬性偏移量肯定就為0

struct TargetStructMetadata {
    var kind: Int
    var descriptor: UnsafeMutablePointer<TargetStructDescriptor>
}

struct TargetStructDescriptor {
    var flags: UInt32
    var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
    var name: TargetRelativeDirectPointer<CChar>
    var accessFunctionPtr: TargetRelativeDirectPointer<UnsafeRawPointer>
    var fields: TargetRelativeDirectPointer<FieldDescriptor>

    var numFields: UInt32
    var fieldOffsetVectorOffset: UInt32
}

struct FieldDescriptor {
    var mangledTypeName: TargetRelativeDirectPointer<UInt8>
    var superClass: TargetRelativeDirectPointer<CChar>
    var kind: UInt16
    var fieldRecordSize: UInt16
    var numField: UInt32
    
    //連續的內存空間存放我們的var變量/case的名稱
    //UnsafeBufferPointer,連續的內存空間指針
    var fieldRecords: FieldRecordBuffer<FieldRecord>
    
    /*
     kind: FieldDescriptorKind(枚舉值依次遞增)
     Struct: 0x1
     Class
     Enum
     MultiPayloadEnum
     Protocol
     ClassProtocol
     ObjCProtocol
     ObjCClass
     */
}

struct FieldRecordBuffer<Element> {
    var element: Element
    
    //方式1.返回buffer連續內存空間
    mutating func buffer(count: Int) -> UnsafeMutableBufferPointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(start: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self), count: count))
        }
    }
    
    //方式2.根據下標獲取數組元素
    mutating func getFieldRecord(index: Int) -> UnsafeMutablePointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutablePointer(mutating: UnsafeRawPointer($0.advanced(by: index)).assumingMemoryBound(to: Element.self))
        }
    }
}

struct FieldRecord {
    var flags: UInt32
    var mangledTypeName: TargetRelativeDirectPointer<UInt8>
    var fieldName: TargetRelativeDirectPointer<CChar>
    
    /*
     flags: FieldRecordFlags
     IsIndirectCase = 0x1,
     IsVar = 0x2,
     IsArtificial = 0x4,
     其它情況 = 0
     */
}

struct TargetRelativeDirectPointer<Pointee> {
    var offset: Int32

    mutating func getMeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
        let offset = self.offset
        return withUnsafePointer(to: &self) {
            /*
             注意:這里的$0必須轉化為UnsafeRawPointer再執行advanced
             如果類型指針advanced會出問題。
             原始是:在介紹指針的時候說過,如果是類型指針的話,
             advanced(x)相當于移動該類型的內存大小*x。
             */
            return UnsafeMutableRawPointer(mutating: UnsafeRawPointer($0).advanced(by: numericCast(offset))).assumingMemoryBound(to: Pointee.self)
        }
    }
}

@_silgen_name("swift_getTypeByMangledNameInContext")
private func getTypeByMangledNameInContext(
    mangledNamePtr: UnsafePointer<UInt8>,
    mangledNameLength: Int,
    genericContext: UnsafeRawPointer?,
    genericArgs: UnsafeRawPointer?) -> Any.Type?

func getTypeByMangleName(
    mangledNamePtr: UnsafePointer<UInt8>,
    genericContext: UnsafeRawPointer?, metadata: UnsafeRawPointer) ->  Any.Type? {
        return getTypeByMangledNameInContext(mangledNamePtr: mangledNamePtr,
                                             mangledNameLength: getMangledTypeNameSize(mangledNamePtr),
                                             genericContext: UnsafeRawPointer(genericContext),
                                             genericArgs: getGenericArgs(metadata))
}

//參考HandyJson,暫時沒找到測量mangledName的長度
func getMangledTypeNameSize(_ managedName: UnsafePointer<UInt8>) -> Int {
    // TODO: should find the actually size 
    return 256
}

//獲取泛型參數在metadata中的偏移量
func getGenericArgumentOffset() -> Int {
    return 2
}

//如果有泛型參數,需要傳入的泛型參數指針。參考HandyJson
func getGenericArgs(_ metadata: UnsafeRawPointer) -> UnsafeRawPointer? {
    let offSetPtr = metadata.advanced(by: getGenericArgumentOffset() * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Int.self)
    if offSetPtr.pointee == 0 {
        return nil
    }
    return UnsafeRawPointer(offSetPtr)
}

main.swift

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

protocol AnyExtensions {}

extension AnyExtensions {
    static func value(ptr: UnsafeRawPointer) -> Any {
        print(self)
        return ptr.assumingMemoryBound(to: self).pointee
    }
}

struct TargetProtocolMetadata {
    let type: Any.Type
    let witness: Int
}

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

print("Struct Kind: 0x\(String(ptr.pointee.kind, radix: 16))") //0x200 => Struct

let descriptorPtr = ptr.pointee.descriptor

print("Struct Name: \(String(cString: descriptorPtr.pointee.name.getMeasureRelativeOffset()))")

let fieldDescriptor = ptr.pointee.descriptor.pointee.fields.getMeasureRelativeOffset()

print("Struct Type: \(getTypeByMangleName(mangledNamePtr: fieldDescriptor.pointee.mangledTypeName.getMeasureRelativeOffset(), genericContext: descriptorPtr, metadata: ptr) as Any)")

/*
 獲取屬性偏移信息
 Struct和Class不一致,Class每8字節存放偏移信息。Struct每4字節存放偏移信息
 */
let offSets = UnsafeRawPointer(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self).advanced(by: numericCast(descriptorPtr.pointee.fieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self)

var t = LGTeacher()

let instanceAddress = withUnsafePointer(to: &t) {$0}

for i in 0..<descriptorPtr.pointee.numFields {
    
    let fieldRecordPtr = fieldDescriptor.pointee.fieldRecords.getFieldRecord(index: numericCast(i))
    let fieldType = getTypeByMangleName(mangledNamePtr: fieldRecordPtr.pointee.mangledTypeName.getMeasureRelativeOffset(), genericContext: descriptorPtr, metadata: ptr)!
    let fieldName = String(cString: fieldRecordPtr.pointee.fieldName.getMeasureRelativeOffset())
    
    //方式1:
//    struct Extensions: AnyExtensions {}
//    var extensions: AnyExtensions.Type = Extensions.self
//    withUnsafePointer(to: &extensions) {
//        UnsafeMutableRawPointer(mutating: $0).assumingMemoryBound(to: Any.Type.self).pointee = fieldType
//    }
//    let value = extensions.value(ptr: UnsafeRawPointer(instanceAddress).advanced(by: Int(offSets[numericCast(i)])))
//    
    //方式2:
    let structMetadata = TargetProtocolMetadata(type: fieldType, witness: 0)
    let protocolType = unsafeBitCast(structMetadata, to: AnyExtensions.Type.self)
    let value = protocolType.value(ptr: UnsafeRawPointer(instanceAddress).advanced(by: Int(offSets[numericCast(i)])))
    
    print("\(fieldName)->\(fieldType)->\(value)")
}

打印信息

Struct Kind: 0x200
Struct Name: LGTeacher
Struct Type: Optional(swiftTest.LGTeacher)
Int
age->Int->18
String
name->String->Kody

問題1:關于fieldOffsetVectorOffset的值

fieldOffsetVectorOffset表示基于matadata的偏移量。如果沒有其它數據的影響的話(比如說繼承,繼承后fieldOffsetVectorOffset+1),就存放在metadata的后面。

TargetClassMetadata大小就是10x8字節,因此對應的fieldOffsetVectorOffset為10。
TargetStructMetadata大小就是2x8字節,因此對應的fieldOffsetVectorOffset為2。

對于偏移值來說

對于Class來說,第一個偏移信息肯定為16。除去HeapObject內存大小
對于Struct來說,第一個偏移信息肯定為0。值類型數據,空間地址就是第一條數據內存空間

問題2:關于變量偏移信息,Class每8字節存放偏移信息。Struct每4字節存放偏移信息

暫未在源碼中找到相應的邏輯

七.關于HandyJson源碼理解

待完善

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容