Swift 五、Mirror源码分析 & StructMetadata还原

Mirror源码分析.png

一、Mirror的基本用法

所谓反射就是可以动态获取类型、成员信息,在运⾏时可以调⽤⽅法、属性等⾏为的特性。在使⽤OC开发时很少强调其反射概念,因为OC的Runtime要⽐其他语⾔中的反射强⼤的多。但是 Swift 是⼀⻔类型安全的语⾔,不⽀持我们像 OC 那样直接操作,它的标准库仍然提供了反射机制来让我们访问成员信息。
Swift 的反射机制是基于⼀个叫 Mirror 的结构体来实现的。为具体的实例创建⼀个Mirror对象,然后就可以通过它查询这个实例。

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

///⾸先通过构造⽅法构建⼀个Mirror实例,这⾥传⼊的参数是 Any,也就意味着当前可以是类,结 构体,枚举等
let mirror = Mirror(reflecting: ZGTeacher())

///接下来遍历 children 属性,这是⼀个集合
for child in mirror.children {
    ///然后我们可以直接通过 label 输出当前的名称,value 输出当前反射的值
    print("\(String(describing: child.label)):\(child.value)")
}

lldb打印

Optional("age"):18

实际使用案例

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)

我们想让我们的函数不管是结构体,类还是枚举还是基础类型也好,都能够具备这个⽅法。这个时候我们是不是就可以⽤协议来做?什么意思那?

首先,我们定义一个协议,这里就表示如果想要具备模型转字典的方式,那么这里就要遵循这个协议,然后实现jsonMap的方法。

protocol ZGJsonMap {
    func jsonMap() -> Any
}

但是大家想一下,这个test方法里面的功能是不是通用的啊,也就意味着我们不需要每一个遵循了ZGJsonMap协议的都自己实现,所以这里我们是不是说可以给这个协议一个默认的实现啊。

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

///我们需要对当前的常见类型和自定义类型遵循JSONMap的协议
///这样对于没有value才能嵌套解析
extension ZGTeacher: JSONMap{}
extension Int: JSONMap{}
extension String: JSONMap{}

var t = ZGTeacher()

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

二、Mirror源码解析

⾸先我们先在源⽂件⾥⾯搜索 Mirror.Swift ,在源码中我们可以很清晰的看到 Mirror 是由结构体实现的,我们忽略掉⼀些细节,快速定位到初始化的⽅法。

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

可以看到,这⾥接受⼀个 Any 类型的参数,同样的这⾥有⼀个 if case 的写法来判断当前的 subject 是否遵循了customReflectable 协议,如果是我们就直接调⽤ customMirror , 否则就进⾏下级函数的调⽤。
这⾥有两个需要注意的点: if case 的写法,这⾥其实枚举 case 的模式匹配,和我们的 Switch ⼀样,这⾥是只有⼀个 case 的 switch 语句。与此同时这⾥出现了⼀个 customRefletable 的协议。
这⾥我们看⼀下具体⽤法: ⾸先我们遵循 customReflectable协议,并实现其中的属性 customMirrorcustomMirror会返回⼀个 Mirror 对象。代码展示如下:

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.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")

实现这个 CustomReflectable 最直观的区别在于我们在 lldb debug 中会出现更详细的 debug 信息。

po t
▿ 
  - age : 18
  - name : "zhang"

回到我们的源码当中,对字符串 Mirror(internalReflecting 在源码当中进⾏检索,我们 就能够快速定位到 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)) 

在当前文件中搜索getNormalizedType

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

这⾥使⽤了⼀个编译器字段 @_silgen_name ,其实是Swift的⼀个隐藏符号,作⽤是将某个C/C++语⾔函数直接映射为Swift函数。
什么意思那?我们的 Swift 是可以和C语⾔进⾏交互的,⽐如我们在C中定义⼀个函数:

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

接下来如果我们的 Swift 想要使⽤这个函数,我们要怎么做?⾸先要在 .h 头⽂件中暴露这个函数:

截屏2022-01-18 17.19.55.png

然后在当前的桥接头⽂件当中暴露


截屏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)

这里一样可以正常调用,是不是很简洁方便哪?
回到我们的源代码,搜索getNormalizedType(_: T, type: Any.Type) -> Any.Type 最终调⽤的是 ReflectionMirror.cpp 中的 C++ 代码。

// func _getNormalizedType(_: 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 函数的实现,这⾥其实是⼀个回调函数,当前回调的具体数据都是由 ReflectionMirrorImpl 结构体实现。

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

ReflectionMirrorImpl 结构体的具体实现(可以看到这是⼀个抽象类,也就意味着不同类型的反射都需要去实现 ReflectionMirrorImpl ),这⾥我们在下⾯的代码中也能看到 class, struct, enum, Tuple的具体实现。

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 为例来看⼀下 Mirror 都是如何获取到这些数据的。
当前的属性数量 (可以看到的是,这⾥通过 Metadata 中的 getDescription查询字段 NumFields )

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

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

其中 getDescription()

const TargetStructDescriptor *getDescription() const {
    return llvm::cast>(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(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(value);
      value = swift_projectBox(const_cast(owner));
    }
    
    *outName = caseName;
    *outFreeFunc = nullptr;
    
    Any result;

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

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

可以看到是这⾥通篇都是通过 Metadata , getDescription() , FieldDescrition 这⼏个东⻄来去实现的,⼀个是当前类型的元数据,⼀个是当前类型的描述,⼀个是对当前类型属性的描述。所以看到这⾥我们能够明⽩ Mirror 是如何⼯作的。
同时我们在前⾯的探索过程中也探索了类,结构体和枚举,接下来我们尝试能不能⾃⼰利⽤ Metadata 来还原获取当前类型的各种属性。

三、StructMetadata源码分析

3.1 TargetStructMetadata

  • 首先我们搜索TargetStructMetadata,找到TargetStructMetadata的定义
template 
struct TargetStructMetadata : public TargetValueMetadata {
    /// 此处会返回一个TargetStructDescriptor类型的Description
  const TargetStructDescriptor *getDescription() const {
    return llvm::cast>(this->Description);
  }

};

TargetStructMetadata.png

可以看出TargetStructMetadata继承自TargetValueMetadata

  • 搜索TargetValueMetadata,找到TargetValueMetadata的定义
struct TargetValueMetadata : public TargetMetadata {
  
  TargetSignedPointer *
  __ptrauth_swift_type_descriptor> Description;

};

TargetValueMetadata.png

我们只看到有一个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

我们只看到它有一个Kind属性。

3.2 TargetValueTypeDescriptor

  • 首先我们根据TargetStructMetadata,找到TargetStructMetadata定义中的TargetStructDescriptor,点击TargetStructDescriptor,找到TargetStructDescriptor的定义
class TargetStructDescriptor final
    : public TargetValueTypeDescriptor,
      public TrailingGenericContextObjects,
                            TargetTypeGenericContextDescriptorHeader,
                            /*additional trailing objects*/
                            TargetForeignMetadataInitialization,
                            TargetSingletonMetadataInitialization,
                            TargetCanonicalSpecializedMetadatasListCount,
                            TargetCanonicalSpecializedMetadatasListEntry,
                            TargetCanonicalSpecializedMetadatasCachingOnceToken> {

public:
 
  uint32_t NumFields;
  
  uint32_t FieldOffsetVectorOffset;
  

};

TargetStructDescriptor.png

TargetStructDescriptor2.png

我们可以发现TargetStructDescriptor有两个属性,分别是NumFieldsFieldOffsetVectorOffset
NumFields主要表示结构体中属性的个数,如果只有一个字段偏移量则表示偏移量的长度。
FieldOffsetVectorOffset表示这个结构体元数据中存储的属性的字段偏移向量的偏移量,如果是0则表示没有。
TargetStructDescriptor 继承自 TargetValueTypeDescriptor

  • 点击TargetValueTypeDescriptor,找到TargetValueTypeDescriptor的定义
class TargetValueTypeDescriptor
    : public TargetTypeContextDescriptor {
public:
  static bool classof(const TargetContextDescriptor *cd) {
    return cd->getKind() == ContextDescriptorKind::Struct ||
           cd->getKind() == ContextDescriptorKind::Enum;
  }
};

TargetValueTypeDescriptor.png

我们并没有找到太多有用的信息,那么我们继续向父类寻找。

  • 点击TargetTypeContextDescriptor,找到TargetTypeContextDescriptor的定义
class TargetTypeContextDescriptor
    : public TargetContextDescriptor {
public:
  
  TargetRelativeDirectPointer Name;

  TargetRelativeDirectPointer AccessFunctionPtr;
  
  TargetRelativeDirectPointer Fields;
 
};

TargetTypeContextDescriptor.png

我们发现TargetTypeContextDescriptorName、AccessFunctionPtr、Fields三个属性:
Name就是类型的名称
AccessFunctionPtr是该类型元数据访问函数的指针
Fields 是一个指向该类型的字段描述符的指针
接下来我们再看看TargetTypeContextDescriptor的父类中还有什么有用的信息

  • 点击TargetContextDescriptor,找到TargetContextDescriptor的定义
struct TargetContextDescriptor {
  
  ContextDescriptorFlags Flags;
  
  TargetRelativeContextPointer Parent;
};

TargetContextDescriptor.png

这里我们可以看到:
1、这就是descriptor的基类
2、有两个属性,分别是FlagsParent
3、Flags是描述上下文的标志,包括它的种类和格式版本。
4、Parent是记录父类上下文的,如果是顶级则为null

3.3 Description中的属性

  • Flags
    Flags.png

    从截图的源码中我们可以看到这个FLags实际是个uint32_t的值,按位存储着kind、isGeneric、isUnique、version、kindSpecificFlags等信息。
  • Parent
    Parent的类型是TargetRelativeContextPointer,我们来看一下TargetRelativeContextPointer,点击跳转过去:
    TargetRelativeContextPointer.png

    我们可以看到TargetRelativeContextPointer是取自RelativeIndirectablePointer的别名,继续点击进行查看:
    RelativeIndirectablePointer.png

    根据注释知道这个类的主要作用是存储在内存中的对象的相对引用。通过RelativeOffsetPlusIndirect属性存储相对的地址偏移量。
    再通过get()函数获取,在get()函数中,会调用applyRelativeOffset函数,进行地址的偏移。
    applyRelativeOffset.png

applyRelativeOffset2.png

最后返回的时候我们可以看到base + extendOffset,基地址加上偏移的值,最后得到真实的地址。

  • Fields
  TargetRelativeDirectPointer Fields;

这里看看FieldDescriptor点击跳转到其源码处,部分源码如下:

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

public:
  const RelativeDirectPointer MangledTypeName;
  const RelativeDirectPointer Superclass;


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

这里有5个属性:

1、MangledTypeName

2、Superclass

3、kind

4、FieldRecordSize

5、NumFields
关于getFieldRecordBuffer函数的返回值FieldRecord源码如下:

FieldRecord.png

FieldRecord主要是封装了一些属性,用于存储这些值。

3.4 type

我们来看一下type是怎么取的:
调用swift_reflectionMirror_normalizedType函数

// func _getNormalizedType(_: 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; });
}

比如说这是个结构体,此时的impl就是个StructImpl类型,所以这里的type是StructImpl父类ReflectionMirrorImpl的属性type

3.5 count

调用swift_reflectionMirror_count函数


// func _getChildCount(_: 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();
  });
}

同样还以结构体为例,此时的implStructImpl,内部的count()函数

structImpl.png

这里的Struct就是个TargetStructMetadata类型,通过getDescription()函数获取到一个TargetStructDescriptor类型的Description,然后取NumFields**的值就是我们要的**count`

3.6 属性名和属性值

我们知道在Mirror中通过其初始化方法返回一个提供该值子元素的AnyCollection类型的children集合,children是一个元组(label: String?, value: Any)label是一个可选类型的属性名,value是属性值。


在分析internalReflecting函数的时候,我们说children是懒加载的,而加载的时候会调用getChild方法,getChild方法源码入下:
getChild.png

getChild方法中还会调用_getChild方法,源码如下:
_getChild.png

_getChild方法同样是使用@_silgen_name修饰符最终调用的C++中的swift_reflectionMirror_subscript函数。

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

这里我们可以看到是调用了implsubscript函数,同样以结构体为例,我们在StructImpl中找到该函数,源码如下:

childMetadata.png

通过subscript函数我们可以看到这里面还会调用childMetadata获取到fieldInfo,其实这里就是获取type,也就是属性名,通过childOffset函数和index获取到对应的偏移量,最后根据内存偏移取到属性值。childMetadata核心点是调用getFieldAt函数获取属性名称

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

我们可以看到在上面这个方法中:

  • 首先通过getTypeContextDescriptor获取baseDesc,也就是我们说的Description
  • 然后通过Fields.get()获取到fields
  • 接着通过getFields()[index]获取对应的field
  • 最后通过getFieldName()函数获取到属性名称
  • getTypeContextDescriptor函数在struct TargetMetadata中,通过这个函数获取到一个TargetStructDescriptor,它的父类的父类TargetTypeContextDescriptor中的Fields属性
  • Fields属性的类型TargetRelativeDirectPointer中有get方法
  • 实际中使用的FieldDescriptor类中getFieldRecordBuffer方法返回的FieldRecord中的getFieldName函数
    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 getFields() const {
    return {getFieldRecordBuffer(), NumFields};
  }

关于getFields我们可以看到这是一块连续的空间,在begin和end中:

  • begin就是getFieldRecordBuffer
  • getFieldRecordBuffer就是Begin + NumFields
  • 所以这就是一块连续内存的访问
    childOffset 源码
    分析完了属性名的获取,我们来看看偏移量的获取。
intptr_t childOffset(intptr_t i) override {
    auto *Clas = static_cast(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(const_cast(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;
  }

这里面是调用TargetStructMetadata中的getFieldOffsets函数源码如下:

const uint32_t *getFieldOffsets() const {
    auto offset = getDescription()->FieldOffsetVectorOffset;
    if (offset == 0)
      return nullptr;
    auto asWords = reinterpret_cast(this);
    return reinterpret_cast(asWords + offset);
  }

我们可以看到这里统一是通过获取Description中的属性,这里使用的属性是FieldOffsetVectorOffset。获取到偏移值后通过内存偏移即可获取到属性值。

四、还原StructMetadata

4.1 TargetStructMetadata

首先我们需要拥有一个结构体的元数据结构,这里我们命名为StructMetadata,里面有继承的kindDescriptor属性,这里的Descriptor属性是一个TargetStructDescriptor类型的指针。

struct TargetStructMetadata {
    var Kind: Int
    var typeDescription: UnsafeMutablePointer
}

4.2 TargetStructDescriptor

对于结构体来说其内部有7个属性
1、flag是个32位的整形,我们用Int32代替
2、parent是记录父类的,类型TargetRelativeDirectPointer,这里也可以用Int32代替
3、name是记录类型的,它的类型是TargetRelativeDirectPointer,所以我们需要实现一个TargetRelativeDirectPointer
4、AccessFunctionPtrname类似,内部是个指针
5、Fields也与name类似,内部是个FieldDescriptor
6、NumFields使用Int32
7、FieldOffsetVectorOffset也是用Int32
仿写实现如下:

struct TargetStructDescriptor {
    var Flags: Int32
    var Parent: Int32
    var Name: TargetRelativeDirectPointer
    var AccessFunctionPtr: TargetRelativeDirectPointer
    var fieldDescriptor: TargetRelativeDirectPointer
    var NumFields: Int32
    /// 每一个属性距离当前实例对象地址的偏移量
    var FieldOffsetVectorOffset: Int32
        
    var genericArgumentOffset: Int {
        return 2
    }
}

4.3 TargetRelativeDirectPointer

  • TargetRelativeDirectPointerRelativeDirectPointer的别名,其内部有一个继承的RelativeOffset属性,是int32_t类型,我们可以用Int32代替。
  • 还有一个get方法,内部通过指针偏移获取值。
struct TargetRelativeDirectPointer {
    var offset: Int32
    ///模拟RelativeDirectPointerImpl类中的get方法 this+offset指针
    mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer {
        let offset = self.offset
        return withUnsafePointer(to: &self) { p in
            /*
             获得self,变为raw,然后+offset
             
             - UnsafeRawPointer(p) 表示this
             - advanced(by: numericCast(offset) 表示移动的步长,即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反射中有着很重要的作用,其内部有5个属性:

  • MangledTypeNameRelativeDirectPointer类型,我们使用TargetRelativeDirectPointer代替
  • SuperclassMangledTypeName一样
  • KindFieldDescriptorKind类型,实际是uint16_t,这里我们使用UInt16代替
  • FieldRecordSizeUInt16也使用使用UInt16代替
  • NumFields使用Int32代替
  • fields,其实从属性上看不到有这个,但是这里面有个getFieldRecordBuffer方法,通过this+1的方式一个一个的访问属性,所以这是一块连续的内存空间,我们使用fields代替
struct FieldDescriptor {
    var MangledTypeName: TargetRelativeDirectPointer
    var Superclass: TargetRelativeDirectPointer
    var Kind: UInt16
    var FieldRecordSize:UInt16
    var NumFields: UInt32
    var fields: FiledRecordBuffer
}

4.5 FieldRecord

FieldRecord存储着属性的相关信息,其内部有三个属性

  • FlagsFieldRecordFlags类型实际是uint32_t,这里我们使用Int32代替
  • MangledTypeName使用TargetRelativeDirectPointer代替
  • FieldName使用TargetRelativeDirectPointer代替
struct FieldRecord {
    var Flags: UInt32
    var MangledTypeName: TargetRelativeDirectPointer
    var FieldName: TargetRelativeDirectPointer
}

struct FiledRecordBuffer {
    var element: Element
    
    mutating func buffer(n: Int) -> UnsafeBufferPointer {
        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 {
        return withUnsafePointer(to: &self) {
            return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self).advanced(by: i))
        }
    }
}

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

定义一个结构体:

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

var p = ZGPerson()

5.1 绑定结构体内存

使用unsafeBitCast按位强转,将ZGPerson绑定到StructMetadata上,注意,这个操作非常危险,没有任何校验和修饰。

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

5.2 打印类名和属性个数

let typeDescription = ptr.pointee.typeDescription

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

let numFields = typeDescription.pointee.NumFields
print("当前类属性的个数:\(numFields)")

5.3 打印属性名称和属性值

1、打印一下属性的名称,首先是获取到FieldDescriptor的指针,然后通过内存偏移的方式访问每一个FieldRecord,最后再访问FieldRecord中的属性名
2、打印属性值:

  • 首先获取FieldOffsetVectorOffset的值
  • 然后再加上this也就是当前Metadata的指针
  • 这里我们将仿写的StructMetadata的指针ptr重绑定为Int
  • 源码中加上FieldOffsetVectorOffset,这里我们就移动FieldOffsetVectorOffset
  • 然后将上述移动后的绑定为一个Int32的指针
  • 最后使用UnsafeBufferPointer属性个数创建一个buffer数组指针
  • 接下来我们就可以从数组中取出每个属性的偏移值
  • 然后取出结构体实例p的内存地址
  • 然后按照buffer数组中的偏移值进行偏移,重绑定为属性的类型
  • 最后就可以打印出属性值
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.size).assumingMemoryBound(to: Any.Type.self)

    let fieldType = swift_getTypeByMangledNameInContext(mangledTypeName, 256, UnsafeRawPointer(typeDescription), UnsafeRawPointer(genericVector).assumingMemoryBound(to: Optional.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")
}

本文部分资料参考了下列文献
作者:愤怒的apple
链接:https://juejin.cn/post/7052644744693809183/
来源:稀土掘金

你可能感兴趣的:(Swift 五、Mirror源码分析 & StructMetadata还原)