Swift-反射Mirror

1. Mirror 简介

MirrorSwift中的反射机制的实现,它的本质是一个结构体。其部分源码(Swift 5.3.1)如下:

**

public struct Mirror {

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

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

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

    /// A mirror of the subject's superclass, if one exists.
    public var superclassMirror: Mirror? {
      return _makeSuperclassMirror()
    }
}
复制代码
  • subjectType:表示类型,被反射主体的类型
  • children:子元素集合
  • displayStyle:显示类型,基本类型为nil 枚举值: struct, class, enum, tuple, optional, collection, dictionary, set
  • superclassMirror:父类反射, 没有父类为nil

除了这些属性还有一些初始化方法,我们最常用的就是初始化方法就是:

**

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

根据源码我们还可以看到能够使用customMirror,这个没太研究,在源码中可以看到很多CustomMirror的身影,感兴趣的可以去研究研究。对于非customMirror的统一使用Mirror(internalReflecting:)进行初始化。

关于customMirror的补充,摘抄自swiftGG Mirror 的工作原理

Mirror 允许类型用遵循 CustomReflectable 协议的方式提供一个自定义的表示方式。这给那些想表示得比内建形式更友好的类型提供一种有效的方法。 比如 Array 类型遵守 CustomReflectable 协议并且暴露其中的元素为无标签的 ChildrenDictionary 使用这种方法暴露其中的键值对为带标签的 Children

2. Mirror的简单使用

2.1 基本使用

这里我们通过使用Mirror打印对象的属性名称和属性值。

**

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

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

print("对象类型:(mirror.subjectType)")
print("对象属性个数:(mirror.children.count)")
print("对象的属性及属性值")
for child in mirror.children {
    print("(child.label!)---(child.value)")
}
复制代码

打印结果:

image.png

我们可以看到,属性名称和值都已经正常打印。

2.2 将对象转换为字典

首先我们来体验一下将对象转换为字典。

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

func mapDic(mirror: Mirror) -> [String: Any] {
    var dic: [String: Any] = [:]
    for child in mirror.children {
        // 如果没有labe就会被抛弃
        if let label = child.label {
            let propertyMirror = Mirror(reflecting: child.value)
            print(propertyMirror)
            dic[label] = child.value
        }
    }
    // 添加父类属性
    if let superMirror = mirror.superclassMirror {
        let superDic = mapDic(mirror: superMirror)
        for p in superDic {
            dic[p.key] = p.value
        }
    }
    return dic
}


// Mirror使用
let cat = Cat()
cat.name = "大橘为重"
let mirror = Mirror(reflecting: cat)
let mirrorDic = mapDic(mirror: mirror)
print(mirrorDic)
复制代码

打印结果:

image.png

通过打印结果我们可以看到,对于一些基本类型,已经可选类型的数据都已经转换为字典值,对于私有属性也可以完成转换。如果里面包含类则还需要进行递归处理。

2.3 转 JSON

注: 这里并没有真正的转换成json字符串,还是只转换成了字典,重要在思想,如果需要转换成json还需要很多优化,以及特殊字符串的考量。

其实提到反射我们想到最多的应该就是JSON了,这里我们利用Mirror的特性,将对象转换成字典,对基本类型和类做了相应的处理,体会一下转json的思路。

首先我们定义一个Person对象,代码如下:

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

struct Address {
    var street: String
}

// 创建一个Person对象
let p = Person()
复制代码

为了通用性,我们可以编写一个协议,用来为所有类型提供转换的方法,只需要遵守该协议就可以使用协议中的方法。

//可以转换为 Json 的协议
protocol CustomJSONProtocol {
    func toJSON() throws -> Any?
}
复制代码

协议的实现过程中会有些错误,我们也简单的定义个枚举,方便处理。为了更加详细的描述错误信息,我们添加了错误描述和错误code。

// 转 json 时的错误类型
enum JSONMapError: Error{
    case emptyKey
    case notConformProtocol
}

// 错误描述
extension JSONMapError: LocalizedError{
    var errorDescription: String?{
        switch self {
        case .emptyKey:
            return "key 为空"
        case .notConformProtocol:
           return "没遵守协议"
        }
    }
}

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

协议实现的代码:

extension CustomJSONProtocol {
    func toJSON() throws -> Any? {
        
        //创建 Mirror 类型
        let mirror = Mirror(reflecting: self)
        // 如果没有属性,比如一般类型String、Int等,直接返回自己
        guard !mirror.children.isEmpty else { return self }
        
        var result: [String:Any] = [:]
        // 遍历
        for children in mirror.children {
            if let value = children.value as? CustomJSONProtocol{
                if let key = children.label {
                    print(key)
                    result[key] = try value.toJSON()
                } else {
                   throw JSONMapError.emptyKey
                }
            } else {
                  throw JSONMapError.notConformProtocol
            }
        }
        
        return result
    }
}
复制代码

将用到的类型都遵守协议

//将一般类型都遵从 CustomJSONProtocol 协议
extension Person: CustomJSONProtocol {}
extension String: CustomJSONProtocol {}
extension Int: CustomJSONProtocol {}
extension Bool: CustomJSONProtocol {}
extension Double: CustomJSONProtocol {}
extension Float: CustomJSONProtocol {}
    
extension Address: CustomJSONProtocol {}

// 数组需要单独处理,要不然就会报错emptyKey
extension Array: CustomJSONProtocol {
    func toJSON() throws -> Any? {
        return self
    }
}

//Optionai 需要特别对待,原因是如果直接返回,则会是 .Some: [...]
extension Optional: CustomJSONProtocol {
    func toJSON() throws -> Any? {
        if let x = self {
            if let value = x as? CustomJSONProtocol {
                return try value.toJSON()
            }
            throw JSONMapError.notConformProtocol
        }
        return nil
    }
}
复制代码

最后我们打印一下:

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

打印结果:

image.png

我们看到,对于some这空值,并没有存储到字典中,因为swift中的字典对于空值是删除的意思。

如果想将其转换成json还需修改"[]"为"{}",这个对于数组和对象还不好区分,另外对于json字符串内的一些value也有可能是应一串json还需要添加转义字符等。

所以总的来说,思路是这样的,要想真正的做成通用的转json的方案还需要很多的优化,比如说,我们不可能将所有的基本类型都去遵守一个协议,这时候我们也可以考虑使用泛型去作为方法的参数。

3. Mirror 源码解析

源码版本Swift 5.3.1

在本章节我们将分析Mirror的部分源码,查看其底层实现,最后通过Swift代码使用内存重绑定的形式,仿写一下Mirror,来更好的探索Mirror的原理,理解Mirror的思想。

我们知道Swift是一门静态语言,那么在底层是如何实现的获取对应的属性值的呢?又或者说Swift的反射特性是如何实现的呢?下面我们通过对Mirror底层源码的探索来寻找答案。

3.1 代码结构

Mirror的实现是由一部分Swift代码加上另一部分C++代码。Swift代码实现在ReflectionMirror.swift文件中,C++代码实现在ReflectionMirror.mm文件中。Swift更适合用在实现更Swift的接口,但是在Swift中不能直接访问C++的类。这里使用了@_silgen_name来实现Swift调用C++中的方法。举个例子:

**

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

@_silgen_name修饰符会通知Swift编译器将这个函数映射成swift_reflectionMirror_count符号,而不是Swift通常对应到的_getChildCount方法名修饰。需要注意的是,最前面的下划线表示这个修饰是被保留在标准库中的。在C++这边,这个函数是这样的。

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

SWIFT_CC(swift)会告诉编译器这个函数使用的是Swift的调用约定,而不是C/C++的,SWIFT_RUNTIME_STDLIB_API标记这个函数,在Swift侧的一部分接口中,而且它还有标记为extern "C"的作用,从而避免C++的方法名修饰,并确保它在Swift侧会有预期的符号。同时C++的参数会去特意匹配在Swift中声明的函数调用。当Swift调用_getChildCount时,C++会用包含Swift值指针的value,包含类型参数type,包含类型响应的泛型<T>T的函数参数来调用此函数。

简单的说就是使用@_silgen_name("xxx")修饰符修饰的Swift方法会调用括号中的xxx的符号,不管是C++的还是C的都可以。

Mirror的在SwiftC++之间的全部接口由以下函数组成:

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

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

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

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

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

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

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

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

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

// Returns 'c' (class), 'e' (enum), 's' (struct), 't' (tuple), or '\0' (none)
@_silgen_name("swift_reflectionMirror_displayStyle")
internal func _getDisplayStyle<T>(_: T) -> CChar

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

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

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

3.2 初始化

在一开始我们简单的介绍了Mirror的部分源码,也由此知道Mirror(reflecting:)初始化方法可以接受任意值,返回一个提供该值子元素集合Children的相关信息的实例。

通过Mirror(reflecting:)源码我们可以知道,其底层调用的是internalReflecting方法。在ReflectionMirror.swift文件的extension Mirror中我们可以找到该方法。其源码如下:

3.2.1 internalReflecting

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

源码分析:

  • 首先是获取subjectType,如果传入的有值就使用传入的值,否则就通过_getNormalizedType函数去获取
  • 接下来就是通过_getChildCount获取childCount
  • 接下来是children,注意这里是懒加载的
  • 紧接着是SuperclassMirror,这里使用的是一个闭包的形式
  • 最后会获取并解析显示的样式,并设置Mirror剩下的属性。

3.2.2 _getNormalizedType

下面我们就来看看_getNormalizedType函数内部的实现。根据上面的分析我们知道实际调用是swift_reflectionMirror_normalizedType。在ReflectionMirror.mm文件中我们可以看到其源码:

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

3.2.3 call函数

我们可以看到这里调用了一个call函数,最后返回的是impltype。首先我们看看这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;
  }
  
  auto call = [&](ReflectionMirrorImpl *impl) {
    impl->type = type;
    impl->value = value;
    auto result = f(impl);
    return result;
  };
  
  auto callClass = [&] {
    if (passedType == nullptr) {
      // Get the runtime type of the object.
      const void *obj = *reinterpret_cast<const void * const *>(value);
      auto isa = _swift_getClass(obj);

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

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

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

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

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

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

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

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

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

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

乍一看这个call函数代码相对有点多,其实主要就是一个大型的switch声明,和一些对特殊情况的处理,这里重要的就是,它会用一个ReflectionMirrorImpl的子类实例去结束调用f,然后会调用这个实例上的方法去让真正的工作完成,这也就是为什么在swift_reflectionMirror_normalizedType函数中最后会return impl->type;感兴趣的可以通过源码去调试一下,这里我也调试了,没什么指的说的,这就就不写调试过程了,下面我们就来看看ReflectionMirrorImpl

3.2.4 ReflectionMirrorImpl

ReflectionMirrorImpl有以下6个子类:

  • TupleImpl 元组的反射
  • StructImpl 结构体的反射
  • EnumImpl 枚举的反射
  • ClassImpl 类的反射
  • MetatypeImpl 元数据的反射
  • OpaqueImpl 不透明类型的反射

ReflectionMirrorImpl 源码:

// Abstract base class for reflection implementations.
struct ReflectionMirrorImpl {
  const Metadata *type;
  OpaqueValue *value;
  
  virtual char displayStyle() = 0;
  virtual intptr_t count() = 0;
  virtual intptr_t childOffset(intptr_t index) = 0;
  virtual const FieldType childMetadata(intptr_t index,
                                        const char **outName,
                                        void (**outFreeFunc)(const char *)) = 0;
  virtual AnyReturn subscript(intptr_t index, const char **outName,
                              void (**outFreeFunc)(const char *)) = 0;
  virtual const char *enumCaseName() { return nullptr; }

#if SWIFT_OBJC_INTEROP
  virtual id quickLookObject() { return nil; }
#endif
  
  // For class types, traverse through superclasses when providing field
  // information. The base implementations call through to their local-only
  // counterparts.
  virtual intptr_t recursiveCount() {
    return count();
  }
  virtual intptr_t recursiveChildOffset(intptr_t index) {
    return childOffset(index);
  }
  virtual const FieldType recursiveChildMetadata(intptr_t index,
                                                 const char **outName,
                                                 void (**outFreeFunc)(const char *))
  {
    return childMetadata(index, outName, outFreeFunc);
  }

  virtual ~ReflectionMirrorImpl() {}
};
复制代码

ReflectionMirrorImpl源码不多,但是我们可以看到type以及count等都在其中。下面我们以结构体为例,看看其子类的源码。

3.2.5 结构体的反射

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

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

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

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

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

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

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

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

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

    return copyFieldContents(fieldData, fieldInfo);
  }
};
复制代码
  • 首先一个判断是否支持反射的方法,最中是访问的Description->isReflectable()
  • 这里使用‘s’来显式的表明这是一个结构体
  • 然后是获取属性个数
  • 紧接着是获取每个属性的偏移值
  • 然后获取了FieldType内部还可以获取到属性的名称。
  • 最后subscript方法可以获取到属性名称和属性偏移的指针,也就是属性值。

3.3 Description

在此处我们看到很多关于Description的代码,看来这个Description存储着很多信息,在获取Description的时候是从StructMetadata通过getDescription()方法获取到。所以这些信息基本确定是从MetaData中获取到的。StructMetadataTargetStructMetadata的别名,我们以此为例:

3.3.1 TargetStructMetadata

TargetStructMetadata我们可以看到如下代码:

**

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

说明此处会返回一个TargetStructDescriptor类型的Description,但是在这里并没有找到这个属性,可能在父类中,我们可以看到TargetStructMetadata继承自TargetValueMetadata

3.3.2 TargetValueMetadata

在这里我们可以看到如下代码:

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

这里我们便找到了:

  • Description属性,它的类型是TargetValueTypeDescriptor,应该是TargetStructDescriptor的父类。
  • getDescription()方法,在TargetStructMetadata是重写的这个方法

3.3.3 TargetStructDescriptor

跳转到TargetStructDescriptor中后,我们可以看到:

  • TargetValueTypeDescriptor果然是它的父类
  • 在其源码中我们还可以发现两个属性,分别是NumFieldsFieldOffsetVectorOffset源码如下:
/// The number of stored properties in the struct.
  /// If there is a field offset vector, this is its length.
  uint32_t NumFields;
  /// The offset of the field offset vector for this struct's stored
  /// properties in its metadata, if any. 0 means there is no field offset
  /// vector.
  uint32_t FieldOffsetVectorOffset;
复制代码
  • 其中NumFields主要表示结构体中属性的个数,如果只有一个字段偏移量则表示偏移量的长度
  • FieldOffsetVectorOffset表示这个结构体元数据中存储的属性的字段偏移向量的偏移量,如果是0则表示没有

3.3.4 TargetValueTypeDescriptor

源码如下,很少:

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

在这里我们并没有找到太多有用的信息,我们继续向父类寻找,其继承自TargetTypeContextDescriptor

3.3.5 TargetTypeContextDescriptor

部分源码:

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

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

我们可以看到:

  • 该类继承自TargetContextDescriptor

  • NameAccessFunctionPtrFields三个属性

    • 其中name就是类型的名称
    • AccessFunctionPtr是该类型元数据访问函数的指针
    • Fields是一个指向该类型的字段描述符的指针

3.3.6 TargetContextDescriptor

接下来我们再看看TargetTypeContextDescriptor的父类中还有什么有用的信息。部分源码如下:

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

这里我们可以看到:

  • 这就是descriptors的基类

  • 有两个属性,分别是FlagsParent

    • 其中Flags是描述上下文的标志,包括它的种类和格式版本。
    • Parent是记录父类上下文的,如果是顶级则为null

3.3.7 小结

至此我们对结构体Description的层级结构基本就理清楚了,现总结如下:

image.png

从上图我们可以看到,对于一个结构体的Description来说,继承链上一共四个类,7个属性。下面我们就对这些属性作进一步的分析

3.4 Description中的属性

3.4.1 Flags

Flags的类型是ContextDescriptorFlags,我们点击跳转进去,我们可以看到:

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

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

从以上的代码中我们可以看到这个FLags实际是个uint32_t的值,按位存储着kindisGenericisUniqueversion等信息。

3.4.2 Parent

Parent的类型是TargetRelativeContextPointer<Runtime>,我们看看TargetRelativeContextPointer,点击跳转过去:

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

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

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

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

根据注释我们就可以轻松的知道这个类的主要作用是存储在内存中的对象的相对引用。这个意思在后面也有相关的解释就是在内存中引用可以是直接的也可以是间接的,直接的就是存储的绝对地址(也不一定,还有ASLR等),直接访问这个地址就可以拿到对应的数据,而这里的相对引用就是间接的,这里面通过RelativeOffsetPlusIndirect属性存储相对的地址偏移量,在通过get()函数获取,在get()函数中,会调用applyRelativeOffset函数,进行地址的偏移,applyRelativeOffset源码:

**

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

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

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

3.4.2 name

name的类型是TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false>,看着应该和Parent差不多。我们点击TargetRelativeDirectPointer跳转到它的源码处:

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

这里依旧是取别名,继续跳转到RelativeDirectPointer,这里有两个选择,我们选择相对引用那个(通过注释区别)。源码如下:

/// 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;
};
复制代码

在源码中我们可以看到很多关于supper的东西,有两处都是通过get()方法返回的,分别是PointerTyValueTy,下面我们就来到父类方法中看看。父类是RelativeDirectPointerImpl,其部分源码如下:

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

这里同样有一个Offset类型RelativeOffset,以及get()方法,同样get()方法也会调用applyRelativeOffset函数进行地址的相加,关于applyRelativeOffset方法在介绍Parent的时候提到过。

这里面多了ValueTyPointerTyValueTy是传入泛型的值,PointerTy是其指针。

name中还有个const char,这里是直接用const char类型存储的类型的名称。

3.4.4 AccessFunctionPtr

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

name一样的使用偏移指针TargetRelativeDirectPointer,只不过是const char替换成了MetadataResponse(...),点击MetadataResponse跳转后我们可以看到如下代码:

MetadataResponse() : Metadata(nullptr) {}

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

所以这里只是一个Metadata的 指针。

3.4.5 Fields

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

TargetRelativeDirectPointer就不多说了,这里看看FieldDescriptor点击跳转到其源码处,部分源码如下:

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

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

  FieldDescriptor() = delete;

  const FieldDescriptorKind Kind;
  const uint16_t FieldRecordSize;
  const uint32_t NumFields;
  
}
复制代码

这里有5个属性:

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

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

class FieldRecord {
  const FieldRecordFlags Flags;

public:
  const RelativeDirectPointer<const char> MangledTypeName;
  const RelativeDirectPointer<const char> FieldName;
.....  
}
复制代码

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

3.4.6 NumFields

NumFields的类型是uint32_t这就没什么好说的了,一个整形存储属性个数

3.4.7 FieldOffsetVectorOffset

FieldOffsetVectorOffset也是个uint32_t的整形,存储偏移量。

3.5 Mirror取值

对于Description分析基本很透彻了,那么我们就回到最初的位置,看看Mirror都是怎样从Description取出相应的值的。

3.5.1 type

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

首先是调用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; });
}
复制代码

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

3.5.2 count

关于count的获取首先是调用swift_reflectionMirror_count函数

// 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();
  });
}
复制代码

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

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

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

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

3.5.3 属性名和属性值

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

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

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

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

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

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

// We intentionally use a non-POD return type with this entry point to give
// it an indirect return ABI for compatibility with Swift.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
// func _getChild<T>(
//   of: T,
//   type: Any.Type,
//   index: Int,
//   outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
//   outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
// ) -> Any
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
AnyReturn swift_reflectionMirror_subscript(OpaqueValue *value, const Metadata *type,
                                           intptr_t index,
                                           const char **outName,
                                           void (**outFreeFunc)(const char *),
                                           const Metadata *T) {
  return call(value, T, type, [&](ReflectionMirrorImpl *impl) {
    return impl->subscript(index, outName, outFreeFunc);
  });
}
#pragma clang diagnostic pop
复制代码

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

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

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

    return copyFieldContents(fieldData, fieldInfo);
  }
复制代码

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

childMetadata源码:

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

这里面的关键点是调用调用getFieldAt函数获取属性名称,

getFieldAt源码:

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

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

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

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

  auto typeName = field.getMangledTypeName();

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

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

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

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

  • 首先通过getTypeContextDescriptor获取baseDesc,也就是我们说的Description
  • 然后通过Fields.get()获取到fields
  • 接着通过getFields()[index]或取对应的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<FieldRecord> getFields() const {
    return {getFieldRecordBuffer(), NumFields};
  }
复制代码

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

  • begin就是getFieldRecordBuffer
  • getFieldRecordBuffer就是Begin + NumFields
  • 所以这就是一块连续内存的访问

childOffset 源码:

分析完了属性名的获取,我们来看看偏移量的获取

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

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

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

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

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

我们可以看到这里统一是通过获取Description中的属性,这里使用的属性是FieldOffsetVectorOffset

获取到偏移值后通过内存偏移即可获取到属性值。

3.6 小结

至此我们对Mirror的原理基本探索完毕了,现在总结一下:

  1. Mirror通过初始化方法返回一会Mirror实例
  2. 这个实例对象根据传入对象的类型去对应的Metadata中找到Description
  3. Description可以获取name也就是属性的名称
  4. 通过内存偏移获取到属性值
  5. 还可以通过numFields获取属性的个数

下面通过该流程图总结一下swift中的mirror对结构体进行反射的主要流程

image.png

关于其他类型的反射也大同小异,还有元组、枚举、类、元数据以及不透明类型的反射,当然也有不完全支持反射的类型,比如结构体就是不完全支持反射的类型,感兴趣的可以继续探索一下。

  • swift中的type(of:)dump(t)就是基于Mirror的反射原理来实现的
  • Swift中的json解析框架HandyJSON的主要原理与Mirror类似,本质上就是利用metadata中的Description,通过字段的访问,做内存的赋值。

猜你喜欢

转载自juejin.im/post/7097422160368500743