Swift中的反射Mirror
[TOC]
前言
Mirror
是Swift
中的反射機制,對于C#
和Java
開發(fā)人員來說秕噪,應(yīng)該很熟悉反射這個概念拘荡。反射就是可以動態(tài)的獲取類型以及成員信息胳徽,同時也可以在運行時動態(tài)的調(diào)用方法和屬性等伍派。
對于iOS
開發(fā)人員來說鸭你,入門時使用的Objective-C
是很少強調(diào)反射概念的屈张,因為OC
的Runtime
要比其他語言的反射強大的多。
另外袱巨,在閱讀本篇文章前建議先看看我的另一篇文章:
Swift 中的類型
1. Mirror 簡介
Mirror
是Swift
中的反射機制的實現(xiàn)阁谆,它的本質(zhì)是一個結(jié)構(gòu)體。其部分源碼(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)
}
}
根據(jù)源碼我們還可以看到能夠使用customMirror
焰盗,這個沒太研究,在源碼中可以看到很多CustomMirror
的身影劝贸,感興趣的可以去研究研究姨谷。對于非customMirror
的統(tǒng)一使用Mirror(internalReflecting:)
進行初始化。
關(guān)于customMirror
的補充映九,摘抄自swiftGG Mirror 的工作原理梦湘。
Mirror
允許類型用遵循 CustomReflectable
協(xié)議的方式提供一個自定義的表示方式。這給那些想表示得比內(nèi)建形式更友好的類型提供一種有效的方法件甥。 比如 Array
類型遵守 CustomReflectable
協(xié)議并且暴露其中的元素為無標簽的 Children
捌议。Dictionary
使用這種方法暴露其中的鍵值對為帶標簽的 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("對象屬性個數(shù):\(mirror.children.count)")
print("對象的屬性及屬性值")
for child in mirror.children {
print("\(child.label!)---\(child.value)")
}
打印結(jié)果:
我們可以看到瓣颅,屬性名稱和值都已經(jīng)正常打印。
2.2 將對象轉(zhuǎn)換為字典
首先我們來體驗一下將對象轉(zhuǎn)換為字典譬正。
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 {
// 如果沒有l(wèi)abe就會被拋棄
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)
打印結(jié)果:
通過打印結(jié)果我們可以看到宫补,對于一些基本類型,已經(jīng)可選類型的數(shù)據(jù)都已經(jīng)轉(zhuǎn)換為字典值曾我,對于私有屬性也可以完成轉(zhuǎn)換粉怕。如果里面包含類則還需要進行遞歸處理。
2.3 轉(zhuǎn) JSON
注: 這里并沒有真正的轉(zhuǎn)換成json字符串抒巢,還是只轉(zhuǎn)換成了字典贫贝,重要在思想,如果需要轉(zhuǎn)換成json還需要很多優(yōu)化,以及特殊字符串的考量稚晚。
其實提到反射我們想到最多的應(yīng)該就是JSON
了崇堵,這里我們利用Mirror
的特性,將對象轉(zhuǎn)換成字典客燕,對基本類型和類做了相應(yīng)的處理鸳劳,體會一下轉(zhuǎn)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
}
// 創(chuàng)建一個Person對象
let p = Person()
為了通用性棍辕,我們可以編寫一個協(xié)議暮现,用來為所有類型提供轉(zhuǎn)換的方法慕爬,只需要遵守該協(xié)議就可以使用協(xié)議中的方法寞冯。
//可以轉(zhuǎn)換為 Json 的協(xié)議
protocol CustomJSONProtocol {
func toJSON() throws -> Any?
}
協(xié)議的實現(xiàn)過程中會有些錯誤,我們也簡單的定義個枚舉,方便處理逢倍。為了更加詳細的描述錯誤信息,我們添加了錯誤描述和錯誤code骤竹。
// 轉(zhuǎn) json 時的錯誤類型
enum JSONMapError: Error{
case emptyKey
case notConformProtocol
}
// 錯誤描述
extension JSONMapError: LocalizedError{
var errorDescription: String?{
switch self {
case .emptyKey:
return "key 為空"
case .notConformProtocol:
return "沒遵守協(xié)議"
}
}
}
// errorcode
extension JSONMapError: CustomNSError{
var errorCode: Int{
switch self {
case .emptyKey:
return 100
case .notConformProtocol:
return 101
}
}
}
協(xié)議實現(xiàn)的代碼:
extension CustomJSONProtocol {
func toJSON() throws -> Any? {
//創(chuàng)建 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
}
}
將用到的類型都遵守協(xié)議
//將一般類型都遵從 CustomJSONProtocol 協(xié)議
extension Person: CustomJSONProtocol {}
extension String: CustomJSONProtocol {}
extension Int: CustomJSONProtocol {}
extension Bool: CustomJSONProtocol {}
extension Double: CustomJSONProtocol {}
extension Float: CustomJSONProtocol {}
extension Address: CustomJSONProtocol {}
// 數(shù)組需要單獨處理踏揣,要不然就會報錯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)
}
打印結(jié)果:
我們看到捞稿,對于some
這空值,并沒有存儲到字典中拼缝,因為swift
中的字典對于空值是刪除的意思娱局。
如果想將其轉(zhuǎn)換成json
還需修改"[]"為"{}",這個對于數(shù)組和對象還不好區(qū)分咧七,另外對于json
字符串內(nèi)的一些value
也有可能是應(yīng)一串json
還需要添加轉(zhuǎn)義字符等衰齐。
所以總的來說,思路是這樣的继阻,要想真正的做成通用的轉(zhuǎn)json
的方案還需要很多的優(yōu)化耻涛,比如說,我們不可能將所有的基本類型都去遵守一個協(xié)議瘟檩,這時候我們也可以考慮使用泛型去作為方法的參數(shù)抹缕。
3. Mirror 源碼解析
源碼版本Swift 5.3.1
在本章節(jié)我們將分析Mirror
的部分源碼,查看其底層實現(xiàn)芒帕,最后通過Swift
代碼使用內(nèi)存重綁定的形式歉嗓,仿寫一下Mirror
,來更好的探索Mirror
的原理背蟆,理解Mirror
的思想鉴分。
我們知道Swift
是一門靜態(tài)語言哮幢,那么在底層是如何實現(xiàn)的獲取對應(yīng)的屬性值的呢?又或者說Swift
的反射特性是如何實現(xiàn)的呢志珍?下面我們通過對Mirror
底層源碼的探索來尋找答案橙垢。
3.1 代碼結(jié)構(gòu)
Mirror
的實現(xiàn)是由一部分Swift
代碼加上另一部分C++
代碼。Swift
代碼實現(xiàn)在ReflectionMirror.swift
文件中伦糯,C++
代碼實現(xiàn)在ReflectionMirror.mm
文件中柜某。Swift
更適合用在實現(xiàn)更Swift
的接口,但是在Swift
中不能直接訪問C++
的類敛纲。這里使用了@_silgen_name
來實現(xiàn)Swift
調(diào)用C++
中的方法喂击。舉個例子:
@_silgen_name("swift_reflectionMirror_count")
internal func _getChildCount<T>(_: T, type: Any.Type) -> Int
@_silgen_name
修飾符會通知Swift
編譯器將這個函數(shù)映射成swift_reflectionMirror_count
符號,而不是Swift
通常對應(yīng)到的_getChildCount
方法名修飾淤翔。需要注意的是翰绊,最前面的下劃線表示這個修飾是被保留在標準庫中的。在C++
這邊旁壮,這個函數(shù)是這樣的监嗜。
// func _getChildCount<T>(_: T, type: Any.Type) -> Int
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
intptr_t swift_reflectionMirror_count(OpaqueValue *value,
const Metadata *type,
const Metadata *T) {
return call(value, T, type, [](ReflectionMirrorImpl *impl) {
return impl->count();
});
}
SWIFT_CC(swift)
會告訴編譯器這個函數(shù)使用的是Swift
的調(diào)用約定,而不是C/C++的抡谐,SWIFT_RUNTIME_STDLIB_API
標記這個函數(shù)裁奇,在Swift
側(cè)的一部分接口中,而且它還有標記為extern "C"
的作用麦撵,從而避免C++
的方法名修飾刽肠,并確保它在Swift
側(cè)會有預(yù)期的符號。同時C++
的參數(shù)會去特意匹配在Swift
中聲明的函數(shù)調(diào)用厦坛。當Swift
調(diào)用_getChildCount
時五垮,C++
會用包含Swift
值指針的value
,包含類型參數(shù)type
杜秸,包含類型響應(yīng)的泛型<T>
的T
的函數(shù)參數(shù)來調(diào)用此函數(shù)放仗。
簡單的說就是使用@_silgen_name("xxx")
修飾符修飾的Swift
方法會調(diào)用括號中的xxx
的符號,不管是C++
的還是C
的都可以撬碟。
Mirror
的在Swift
和C++
之間的全部接口由以下函數(shù)組成:
@_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
的相關(guān)信息的實例呢蛤。
通過Mirror(reflecting:)
源碼我們可以知道惶傻,其底層調(diào)用的是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
函數(shù)去獲取 - 接下來就是通過
_getChildCount
獲取childCount
- 接下來是
children
,注意這里是懶加載的 - 緊接著是
SuperclassMirror
蜈敢,這里使用的是一個閉包的形式 - 最后會獲取并解析顯示的樣式辜荠,并設(shè)置
Mirror
剩下的屬性。
3.2.2 _getNormalizedType
下面我們就來看看_getNormalizedType
函數(shù)內(nèi)部的實現(xiàn)抓狭。根據(jù)上面的分析我們知道實際調(diào)用是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函數(shù)
我們可以看到這里調(diào)用了一個call
函數(shù),最后返回的是impl
的type
否过。首先我們看看這call
函數(shù):
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
函數(shù)代碼相對有點多午笛,其實主要就是一個大型的switch
聲明,和一些對特殊情況的處理苗桂,這里重要的就是药磺,它會用一個ReflectionMirrorImpl
的子類實例去結(jié)束調(diào)用f
,然后會調(diào)用這個實例上的方法去讓真正的工作完成誉察,這也就是為什么在swift_reflectionMirror_normalizedType
函數(shù)中最后會return impl->type;
感興趣的可以通過源碼去調(diào)試一下与涡,這里我也調(diào)試了,沒什么指的說的持偏,這就就不寫調(diào)試過程了,下面我們就來看看ReflectionMirrorImpl
氨肌。
3.2.4 ReflectionMirrorImpl
ReflectionMirrorImpl
有以下6個子類:
- TupleImpl 元組的反射
- StructImpl 結(jié)構(gòu)體的反射
- EnumImpl 枚舉的反射
- ClassImpl 類的反射
- MetatypeImpl 元數(shù)據(jù)的反射
- 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
等都在其中。下面我們以結(jié)構(gòu)體為例怎囚,看看其子類的源碼卿叽。
3.2.5 結(jié)構(gòu)體的反射
// 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’
來顯式的表明這是一個結(jié)構(gòu)體 - 然后是獲取屬性個數(shù)
- 緊接著是獲取每個屬性的偏移值
- 然后獲取了
FieldType
內(nèi)部還可以獲取到屬性的名稱恳守。 - 最后
subscript
方法可以獲取到屬性名稱和屬性偏移的指針考婴,也就是屬性值。
3.3 Description
在此處我們看到很多關(guān)于Description
的代碼催烘,看來這個Description
存儲著很多信息沥阱,在獲取Description
的時候是從StructMetadata
通過getDescription()
方法獲取到。所以這些信息基本確定是從MetaData
中獲取到的伊群。StructMetadata
是TargetStructMetadata
的別名考杉,我們以此為例:
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
枕稀,應(yīng)該是TargetStructDescriptor
的父類。 -
getDescription()
方法,在TargetStructMetadata
是重寫的這個方法
3.3.3 TargetStructDescriptor
跳轉(zhuǎn)到TargetStructDescriptor
中后萎坷,我們可以看到:
-
TargetValueTypeDescriptor
果然是它的父類 - 在其源碼中我們還可以發(fā)現(xiàn)兩個屬性范抓,分別是
NumFields
和FieldOffsetVectorOffset
源碼如下:
/// 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
主要表示結(jié)構(gòu)體中屬性的個數(shù),如果只有一個字段偏移量則表示偏移量的長度 -
FieldOffsetVectorOffset
表示這個結(jié)構(gòu)體元數(shù)據(jù)中存儲的屬性的字段偏移向量的偏移量匕垫,如果是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;
}
};
在這里我們并沒有找到太多有用的信息,我們繼續(xù)向父類尋找偶惠,其繼承自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
- 有
Name
朗涩、AccessFunctionPtr
忽孽、Fields
三個屬性- 其中
name
就是類型的名稱 -
AccessFunctionPtr
是該類型元數(shù)據(jù)訪問函數(shù)的指針 -
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
的基類 - 有兩個屬性出革,分別是
Flags
和Parent
- 其中
Flags
是描述上下文的標志骂束,包括它的種類和格式版本展箱。 -
Parent
是記錄父類上下文的混驰,如果是頂級則為null
- 其中
3.3.7 小結(jié)
至此我們對結(jié)構(gòu)體Description
的層級結(jié)構(gòu)基本就理清楚了账胧,現(xiàn)總結(jié)如下:
從上圖我們可以看到,對于一個結(jié)構(gòu)體的Description
來說遮精,繼承鏈上一共四個類准脂,7個屬性狸膏。下面我們就對這些屬性作進一步的分析
3.4 Description中的屬性
3.4.1 Flags
Flags
的類型是ContextDescriptorFlags
,我們點擊跳轉(zhuǎn)進去贤旷,我們可以看到:
/// 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
的值幼驶,按位存儲著kind
盅藻、isGeneric
氏淑、isUnique
夸政、version
等信息。
3.4.2 Parent
Parent
的類型是TargetRelativeContextPointer<Runtime>
坑资,我們看看TargetRelativeContextPointer
袱贮,點擊跳轉(zhuǎn)過去:
using TargetRelativeContextPointer =
RelativeIndirectablePointer<const Context<Runtime>,
/*nullable*/ true, int32_t,
TargetSignedContextPointer<Runtime, Context>>;
我們可以看到TargetRelativeContextPointer
是取自RelativeIndirectablePointer
的別名攒巍,繼續(xù)點擊進行查看:
/// 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);
}
}
}
根據(jù)注釋我們就可以輕松的知道這個類的主要作用是存儲在內(nèi)存中的對象的相對引用。這個意思在后面也有相關(guān)的解釋就是在內(nèi)存中引用可以是直接的也可以是間接的兢孝,直接的就是存儲的絕對地址(也不一定雳殊,還有ASLR
等)夯秃,直接訪問這個地址就可以拿到對應(yīng)的數(shù)據(jù)仓洼,而這里的相對引用就是間接的衬潦,這里面通過RelativeOffsetPlusIndirect
屬性存儲相對的地址偏移量镀岛,在通過get()
函數(shù)獲取,在get()函數(shù)中走越,會調(diào)用applyRelativeOffset
函數(shù)旨指,進行地址的偏移谆构,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>
,看著應(yīng)該和Parent
差不多皂吮。我們點擊TargetRelativeDirectPointer
跳轉(zhuǎn)到它的源碼處:
template <typename Runtime, typename Pointee, bool Nullable = true>
using TargetRelativeDirectPointer
= typename Runtime::template RelativeDirectPointer<Pointee, Nullable>;
這里依舊是取別名蜂筹,繼續(xù)跳轉(zhuǎn)到RelativeDirectPointer
艺挪,這里有兩個選擇口蝠,我們選擇相對引用那個(通過注釋區(qū)別)妙蔗。源碼如下:
/// 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;
};
在源碼中我們可以看到很多關(guān)于supper
的東西,有兩處都是通過get()
方法返回的寸五,分別是PointerTy
和ValueTy
梳杏,下面我們就來到父類方法中看看。父類是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()
方法也會調(diào)用applyRelativeOffset
函數(shù)進行地址的相加鄙才,關(guān)于applyRelativeOffset
方法在介紹Parent
的時候提到過攒庵。
這里面多了ValueTy
和PointerTy
浓冒,ValueTy
是傳入泛型的值闲擦,PointerTy
是其指針墅冷。
name
中還有個const char
,這里是直接用const char
類型存儲的類型的名稱腔彰。
3.4.4 AccessFunctionPtr
AccessFunctionPtr
的類型是TargetRelativeDirectPointer<Runtime, MetadataResponse(...), /*Nullable*/ true>
跟name
一樣的使用偏移指針TargetRelativeDirectPointer
萍桌,只不過是const char
替換成了MetadataResponse(...)
上炎,點擊MetadataResponse
跳轉(zhuǎn)后我們可以看到如下代碼:
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
點擊跳轉(zhuǎn)到其源碼處,部分源碼如下:
// 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個屬性:
MangledTypeName
Superclass
kind
FieldRecordSize
NumFields
關(guān)于getFieldRecordBuffer
函數(shù)的返回值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
這就沒什么好說的了甲抖,一個整形存儲屬性個數(shù)
3.4.7 FieldOffsetVectorOffset
FieldOffsetVectorOffset
也是個uint32_t
的整形挫剑,存儲偏移量暮顺。
3.5 Mirror取值
對于Description
分析基本很透徹了捶码,那么我們就回到最初的位置,看看Mirror
都是怎樣從Description
取出相應(yīng)的值的祈纯。
3.5.1 type
首先我們看看type
是怎么取的:
首先是調(diào)用swift_reflectionMirror_normalizedType
函數(shù)
// func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
const Metadata *swift_reflectionMirror_normalizedType(OpaqueValue *value,
const Metadata *type,
const Metadata *T) {
return call(value, T, type, [](ReflectionMirrorImpl *impl) { return impl->type; });
}
比如說這是個結(jié)構(gòu)體,此時的impl
就是個StructImpl
類型簇爆,所以這里的type
是StructImpl
父類ReflectionMirrorImpl
的屬性type
入蛆。
3.5.2 count
關(guān)于count
的獲取首先是調(diào)用swift_reflectionMirror_count
函數(shù)
// func _getChildCount<T>(_: T, type: Any.Type) -> Int
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
intptr_t swift_reflectionMirror_count(OpaqueValue *value,
const Metadata *type,
const Metadata *T) {
return call(value, T, type, [](ReflectionMirrorImpl *impl) {
return impl->count();
});
}
同樣還以結(jié)構(gòu)體為例,此時的impl
為StructImpl
扼褪,內(nèi)部的count()
函數(shù):
intptr_t count() {
if (!isReflectable()) {
return 0;
}
auto *Struct = static_cast<const StructMetadata *>(type);
return Struct->getDescription()->NumFields;
}
這里的Struct
就是個TargetStructMetadata
類型,通過getDescription()
函數(shù)獲取到一個TargetStructDescriptor
類型的Description
,然后取NumFields
的值就是我們要的count
跋核。
3.5.3 屬性名和屬性值
我們知道在Mirror
中通過其初始化方法返回一個提供該值子元素的AnyCollection<Child>
類型的children
集合蹋订,Child
是一個元組(label: String?, value: Any)
露戒,label
是一個可選類型的屬性名,value
是屬性值荠锭。
在分析internalReflecting
函數(shù)的時候证九,我們說children
是懶加載的,而加載的時候會調(diào)用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
方法中還會調(diào)用_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
修飾符最終調(diào)用的C++
中的swift_reflectionMirror_subscript
函數(shù)惨奕。
// 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
這里我們可以看到是調(diào)用了impl
的subscript
函數(shù),同樣以結(jié)構(gòu)體為例卧波,我們在StructImpl
中找到該函數(shù),源碼如下:
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
函數(shù)我們可以看到這里面還會調(diào)用childMetadata
獲取到fieldInfo
寸宏,其實這里就是獲取type
氮凝,也就是屬性名罩阵,通過childOffset
函數(shù)和index
獲取到對于的偏移量,最后根據(jù)內(nèi)存偏移去到屬性值常摧。
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;
}
這里面的關(guān)鍵點是調(diào)用調(diào)用getFieldAt
函數(shù)獲取屬性名稱,
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]
或取對應(yīng)的field
最后通過
getFieldName()
函數(shù)獲取到屬性名稱getTypeContextDescriptor
函數(shù)在struct TargetMetadata
中梳侨,通過這個函數(shù)獲取到一個
TargetStructDescriptor
走哺,它的父類的父類TargetTypeContextDescriptor
中的Fields
屬性Fields
屬性的類型TargetRelativeDirectPointer
中有get
方法實際中使用的
FieldDescriptor
類中getFieldRecordBuffer
方法返回的FieldRecord
中的getFieldName
函數(shù)
getFields 源碼:
const_iterator begin() const {
auto Begin = getFieldRecordBuffer();
auto End = Begin + NumFields;
return const_iterator { Begin, End };
}
const_iterator end() const {
auto Begin = getFieldRecordBuffer();
auto End = Begin + NumFields;
return const_iterator { End, End };
}
llvm::ArrayRef<FieldRecord> getFields() const {
return {getFieldRecordBuffer(), NumFields};
}
關(guān)于getFields
我們可以看到這是一塊連續(xù)的空間,在begin
和end
中:
-
begin
就是getFieldRecordBuffer
-
getFieldRecordBuffer
就是Begin + NumFields
- 所以這就是一塊連續(xù)內(nèi)存的訪問
childOffset 源碼:
分析完了屬性名的獲取晒旅,我們來看看偏移量的獲取
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];
}
這里面是調(diào)用TargetStructMetadata
中的getFieldOffsets
函數(shù)源碼如下:
/// 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);
}
我們可以看到這里統(tǒng)一是通過獲取Description
中的屬性,這里使用的屬性是FieldOffsetVectorOffset
孝常。
獲取到偏移值后通過內(nèi)存偏移即可獲取到屬性值。
3.6 小結(jié)
至此我們對Mirror
的原理基本探索完畢了岸梨,現(xiàn)在總結(jié)一下:
-
Mirror
通過初始化方法返回一會Mirror實例
- 這個實例對象根據(jù)傳入對象的類型去對應(yīng)的
Metadata
中找到Description
- 在
Description
可以獲取name
也就是屬性的名稱 - 通過內(nèi)存偏移獲取到屬性值
- 還可以通過
numFields
獲取屬性的個數(shù)
下面通過該流程圖總結(jié)一下swift
中的mirror
對結(jié)構(gòu)體進行反射的主要流程
關(guān)于其他類型的反射也大同小異隔披,還有元組奢米、枚舉谒拴、類英上、元數(shù)據(jù)以及不透明類型的反射苍日,當然也有不完全支持反射的類型,比如結(jié)構(gòu)體就是不完全支持反射的類型豆茫,感興趣的可以繼續(xù)探索一下。
-
swift
中的type(of:)
火脉、dump(t)
就是基于Mirror
的反射原理來實現(xiàn)的 -
Swift
中的json
解析框架HandyJSON
的主要原理與Mirror
類似畸颅,本質(zhì)上就是利用metadata
中的Description
没炒,通過字段的訪問,做內(nèi)存的賦值种吸。
4. 仿寫 Mirror
為了加深對Mirror
的理解坚俗,我們使用Swift
語言仿寫一下。還是以結(jié)構(gòu)體為例辙浑。
4.1 TargetStructMetadata
首先我們需要擁有一個結(jié)構(gòu)體的元數(shù)據(jù)結(jié)構(gòu),這里我們命名為StructMetadata
侠草,里面有繼承的kind
和Descriptor
屬性,這里的Descriptor
屬性是一個TargetStructDescriptor
類型的指針功蜓。仿寫代碼如下:
struct StructMetadata{
var kind: Int
var Descriptor: UnsafeMutablePointer<StructDescriptor>
}
4.2 TargetStructDescriptor
在4.1
中我們用到的Descriptor
屬性的內(nèi)部結(jié)構(gòu)現(xiàn)在也來實現(xiàn)一下。這個是Mirror
中用到很多的屬性著隆。對于結(jié)構(gòu)體來說其內(nèi)部有7個屬性
-
flag
是個32位的整形弦赖,我們用Int32
代替 -
parent
是記錄父類的蹬竖,類型是TargetRelativeContextPointer<Runtime>
,這里也可以用Int32
代替 -
name
記錄類型的流酬,它的類型是TargetRelativeDirectPointer<char>
案腺,所以我們需要實現(xiàn)一個TargetRelativeDirectPointer
-
AccessFunctionPtr
與name
類似,內(nèi)部是個指針 -
Fields
也與name
類似康吵,內(nèi)部是個FieldDescriptor
-
NumFields
使用Int32
-
FieldOffsetVectorOffset
也是用Int32
仿寫實現(xiàn)如下:
struct StructDescriptor {
let flags: Int32
let parent: Int32
var name: RelativePointer<CChar>
var AccessFunctionPtr: RelativePointer<UnsafeRawPointer>
var Fields: RelativePointer<FieldDescriptor>
var NumFields: Int32
var FieldOffsetVectorOffset: Int32
}
4.3 TargetRelativeDirectPointer
-
TargetRelativeDirectPointer
是RelativeDirectPointer
的別名访递,其內(nèi)部有一個繼承的RelativeOffset
屬性晦嵌,是int32_t
類型,我們可以用Int32
代替拷姿。 - 還有一個
get
方法惭载。內(nèi)部通過指針偏移獲取值
仿寫實現(xiàn)如下:
struct RelativePointer<T> {
var offset: Int32
mutating func get() -> UnsafeMutablePointer<T>{
let offset = self.offset
return withUnsafePointer(to: &self) { p in
return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
}
}
}
4.4 FieldDescriptor
FieldDescriptor
在Mirror
反射中有著很重要的作用,其內(nèi)部有5個屬性:
-
MangledTypeName
是RelativeDirectPointer<const char>
類型响巢,我們使用RelativePointer<CChar>
代替 -
Superclass
與MangledTypeName
一樣 -
kind
是FieldDescriptorKind
類型描滔,實際是uint16_t
,這里我們使用UInt16
代替 -
fieldRecordSize
是uint16_t
也使用使用UInt16
代替 -
numFields
使用Int32
代替 -
fields
踪古,其實從屬性上看不到有這個含长,但是這里面有個getFieldRecordBuffer
方法,通過this+1
的方式一個一個的訪問屬性伏穆,所以這是一塊連續(xù)的內(nèi)存空間拘泞,我們使用fields
代替
仿寫代碼如下:
struct FieldDescriptor {
var MangledTypeName: RelativePointer<CChar>
var Superclass: RelativePointer<CChar>
var kind: UInt16
var fieldRecordSize: Int16
var numFields: Int32
var fields: FieldRecord //連續(xù)的存儲空間
}
4.5 FieldRecord
FieldRecord
存儲著屬性的相關(guān)信息,其內(nèi)部有三個屬性
-
Flags
是FieldRecordFlags
類型實際是uint32_t
枕扫,這里我們使用Int32
代替 -
MangledTypeName
使用RelativePointer<CChar>
代替 -
FieldName
使用RelativePointer<CChar>
代替
仿寫帶如下:
struct FieldRecord {
var Flags: Int32
var MangledTypeName: RelativePointer<CChar>
var FieldName: RelativePointer<CChar>
}
4.6 測試
下面我們使用內(nèi)存綁定的計數(shù)訪問一個結(jié)構(gòu)體
定義一個結(jié)構(gòu)體:
struct Person {
var name: String = "xiaohei"
var age: Int = 18
var height = 1.85
}
var p = Person()
4.6.1 綁定結(jié)構(gòu)體內(nèi)存
使用unsafeBitCast
按位強轉(zhuǎn)陪腌,將Person
綁定到StructMetadata
上,這個操作非常危險烟瞧,沒有任何校驗和修飾
let ptr = unsafeBitCast(Person.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)
4.6.2 打印類型和屬性個數(shù)
下面我們就打印一下結(jié)構(gòu)體的類型(也就是它的名稱)和其中屬性的個數(shù):
let namePtr = ptr.pointee.Descriptor.pointee.name.get()
print(String(cString: namePtr))
print(ptr.pointee.Descriptor.pointee.NumFields)
打印結(jié)果:
這里我們就可以看到結(jié)構(gòu)體的名稱和其屬性個數(shù)的正確打印了诗鸭。
4.6.3 打印屬性名稱
下面我們就來打印一下屬性的名稱,首先是獲取到FieldDescriptor
的指針参滴,然后通過內(nèi)存偏移的方式訪問每一個FieldRecord
强岸,最后在訪問FieldRecord
中的屬性名。代碼如下:
let fieldDescriptorPtr = ptr.pointee.Descriptor.pointee.Fields.get()
let recordPtr = withUnsafePointer(to: &fieldDescriptorPtr.pointee.fields) {
return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: FieldRecord.self).advanced(by: 2))
}
print(String(cString: recordPtr.pointee.FieldName.get()))
打印結(jié)果:
此時我們就可以看到第三屬性height
的打印卵洗,如果advanced(by: 0)
則打印第一個屬性请唱,以此類推弥咪。
4.6.1 打印屬性值
下面我們訪問一下屬性值:
首先是獲取屬性偏移量的數(shù)組,也就是getFieldOffsets
函數(shù)返回的值十绑。根據(jù)源碼:
- 首先獲取
FieldOffsetVectorOffset
的值 - 然后在加上
this
也就是當前Metadata
的指針 - 這里我們將仿寫的
StructMetadata
的指針ptr
重綁定為Int
- 源碼中加上
FieldOffsetVectorOffset
聚至,這里我們就移動FieldOffsetVectorOffset
- 然后將上述移動后的綁定為一個
Int32
的指針 - 最后使用
UnsafeBufferPointer
和屬性個數(shù)創(chuàng)建一個buffer
數(shù)組指針 - 接下來我們就可以從數(shù)組中取出每個屬性的偏移值
- 然后取出結(jié)構(gòu)體實例
p
的內(nèi)存地址 - 然后按照
buffer
數(shù)組中的偏移值進行偏移,重綁定為屬性的類型 - 最后就可以打印出屬性值了
實現(xiàn)代碼:
var bufferPtr = UnsafeBufferPointer(start: UnsafeRawPointer(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self).advanced(by: numericCast(ptr.pointee.Descriptor.pointee.FieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self), count: Int(ptr.pointee.Descriptor.pointee.NumFields))
var fieldOffset = bufferPtr[2]
var valuePtr = withUnsafeMutablePointer(to: &p) { $0 }
var bufferPtr1 = UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(bufferPtr[0]))).assumingMemoryBound(to: String.self)
print(bufferPtr1.pointee)
var bufferPtr2 = UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(bufferPtr[1]))).assumingMemoryBound(to: Int.self)
print(bufferPtr2.pointee)
var bufferPtr3 = UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(bufferPtr[2]))).assumingMemoryBound(to: Double.self)
print(bufferPtr3.pointee)
打印結(jié)果: