一.元類型、.self和Self
1.AnyObject
AnyObject
代表任意類的實例,類的類型,僅類遵循的協議。
//3.僅類遵循的協議
protocol Myprotocol: AnyObject {
func test()
}
class LGTeacher {
var age = 18
var name = "Kody"
}
let t = LGTeacher()
//1.代表實例
var t1: AnyObject = t
//2.代表類的類型
var t2: AnyObject = LGTeacher.self
2.T.self
T.self
如果T
是實例對象,T.self
返回的就是實例本身。如果T
是類,返回的就是元類型
//實例對象
let t = LGTeacher() //<LGTeacher: 0x101334100>
//實例對象
let t1 = t.self //<LGTeacher: 0x101334100>
//元類型(metadata)
let t2 = LGTeacher.self //(@thick swiftTest.LGTeacher.Type) t2 = swiftTest.LGTeacher
匯編探究swiftTest.LGTeacher
- 此時的
rax
其實就是元類型,也就是LGTeacher
的metadata
3.self在不同方法里的表現
class LGTeacher {
var age = 18
var name = "Kody"
func test() {
//當前實例對象
print(self)
}
static func test1() {
//self是LGTeacher這個類型本身,也就是metadata
print(self)
}
}
4.Self
//2.遵循協議的類型
protocol Myprotocol: AnyObject {
func get() -> Self
}
class LGTeacher: Myprotocol {
static let age = 18
//3.訪問類型屬性(Swift5.5已經不能在存儲屬性上使用Self),但是在函數內訪問類型屬性是可以的(第4點)。
// let age1 = Self.age
//1.作為方法的返回類型
func test() -> Self {
return self
}
func get() -> Self {
return self
}
static func getAge() -> Int {
//4.訪問類型屬性
return Self.age
}
func getAge() -> Int {
//4.訪問類型屬性
return Self.age
}
}
5.Any&AnyClass
Any
表示任意類型,包括function
類型或者Optional
類型(Swift里面函數/可選值也是有metadata
的)
AnyClass
代表任意實例的類型。AnyObject.Type
class LGTeacher {
var age = 18
}
//AnyClass: AnyObject.Type(任意實例的類型)
//LGTeacher.self(屬于LGTeacher.Type)
//而LGTeacher又屬于AnyObject,因此這樣的寫法是沒有問題的
var t: AnyClass = LGTeacher.self
//這樣寫是不行的,LGTeacher.Type表示LGTeacher.Type.Type,而AnyClass為LGTeacher.Type
//var t1: AnyClass = LGTeacher.Type
6.type(of: T)
獲取一個值的動態類型
var age = 10
//Value的靜態類型:Any
func test(_ value: Any) {
print(type(of: value)) // Int
}
//self --> type(of:T)
//在我們使用self時,相當于就是type(of:)獲取實際類型
test(age)
例子
protocol Myprotocol: AnyObject {
func get() -> Self
}
class LGTeacher: Myprotocol {
var age = 18
//typeof(:self)
func get() -> Self {
return self
}
}
//靜態類型為Myprotocol
let t: Myprotocol = LGTeacher()
//打印的是實際類型
print(t.get()) //swiftTest.LGTeacher
7.探究一個在stackoverflow上的一個問題
protocol P {
init()
}
extension P {
static func createWithBigSelf() -> Self {
return Self()
}
static func createWithLittleSelf() -> Self {
return self.init()
}
}
class A : P {
required init() {}
}
class B : A {}
let x: A.Type = B.self
print(x.createWithBigSelf()) // A
print(x.createWithLittleSelf()) // B
/*
第一點,要想在函數內使用Self(),必須要實現required init函數。
執行createWithBigSelf時,相當于執行了x的靜態類型傳入了函數內,Self()中的Self指的是當前類型,
也就是通過傳入的類型創建了一個當前實例,因此x.createWithBigSelf()為A
執行createWithLittleSelf時,執行到self.init()時,其實相當于執行了type(of:T),獲取到了真實類型去構造了一個實例,
因此x.createWithLittleSelf()為B
當然,這里只是個人角度上分析,也可以從SIL或IR的角度上分析可能更直觀。
*/
二.Swift Runtime
我們在Swift -- 2.類與結構體(下)探究影響函數的派發方式
也說了一些關于在Swift
上使用Runtime
功能,必須加上@objc dynamic
@objc
暴露給Runtime Api
。消息不會變為消息調度機制objc_msgSend
dynamic
賦予動態性
繼承NSObject的類
才能在OC中使用
- 對于純
Swift
類來說,方法和屬性不加任何修飾符的情況下。這個時候其實已經不具備我們所謂的Runtime
特性了 - 對于純
Swift
類來說,方法和屬性添加@objc
,我們當前可以通過Runtime Api
拿到,但是沒法進行調度,因為并沒有賦予它動態性 - 對于純
Swift
類來說,沒有動態性,可以添加dynamic
修飾,可獲得動態性 - 對于純
Swift
類來說,方法和屬性添加@objc dynamic
,可以使用Runtime Api
進行調度。但是OC代碼中不能使用,因為沒有繼承自NSObject
- 繼承自
NSObject
的Swift
類,如果想要動態獲取當前的屬性和方法,必須在其聲明之前添加@objc
關鍵字,否則也是沒有辦法通過Runtime Api
獲取的 - 繼承自
NSObject
的Swift
類,其繼承自父類的方法具有動態性,其它自定義方法、屬性想要獲得動態性,需要添加dynamic
修飾
- 若方法的參數、屬性類型為
Swift
特有、無法映射到Objective-C
的類型(如果Character、Tuple),則此方法、屬性無法添加dynamic
修飾(編譯器報錯)
總結:簡單的來說,如果對于Swift
來說如果想使用Runtime Api
必須加上@objc
,如果想要動態性加上dynamic
,如果想要在OC上使用加上NSObject
三.Mirror
所謂反射就是可以動態獲取類型、成員信息,在運行時可以調用方法、屬性等行為的特性。在使用OC開發時很少強調其反射概念,因為OC的Runtime
要比其它語言中的反射強大得多。但是Swift
是一門類型安全的語言,不支持我們像OC那樣直接操作,它的標準庫依然提供了反射機制來讓我們訪問成員信息。
Swift
的反射機制是基于一個叫Mirror
的結構體實現的。你為具體的實例創建一個Mirror對象
,然后可以通過它查詢這個實例
1.基本用法
class LGTeacher {
var age = 18
func teach() {
print("teach")
}
}
//構建一個Mirror實例,傳入的參數為Any類型
let mirror = Mirror(reflecting: LGTeacher())
/*
為什么傳入LGTeacher.self,mirror.children是一個空集合?
因為傳入進去的是一個類型(傳入進去的是metadata),對于元類型來說是沒有屬性和方法,因此也不會反射出數據
*/
//注意:當前通過Mirror反射是沒法反射到函數的
for pro in mirror.children {
//pro.label,當前的名稱
//pro.value,反射的值
print("\(pro.label):\(pro.value)") // Optional("age"):18
}
//拿到類名字符串
let className = Mirror(reflecting: LGTeacher()).description.replacingOccurrences(of: "Mirror for", with: "").trimmingCharacters(in: CharacterSet.whitespaces)
2.小案例:將映射中的屬性存放字典中
class LGTeacher {
var age = 18
}
func testMirror(_ obj: Any) -> Any {
let mirror = Mirror(reflecting: obj)
guard !mirror.children.isEmpty else {
return obj
}
var dic = [String:Any]()
for child in mirror.children {
if let key = child.label {
dic[key] = testMirror(child.value)
}else {
print("No Key")
}
}
return dic
}
let result = testMirror(LGTeacher())
print(result) //["age": 18]
那么如果我們想讓所有的類都可以實現這個功能,此時可以用protocol
協議實現
class LGTeacher {
var age = 18
}
protocol JsonMap {
func testMirror() -> Any
}
extension JsonMap {
func testMirror() -> Any {
let mirror = Mirror(reflecting: self)
guard !mirror.children.isEmpty else {
return self
}
var dic = [String:Any]()
for child in mirror.children {
if let value = child.value as? JsonMap {
if let key = child.label {
dic[key] = value.testMirror()
}else {
print("No Key")
}
}else {
print("child.value not comfirm JsonMap Protocol")
}
}
return dic
}
}
extension LGTeacher: JsonMap{}
extension Int: JsonMap {}
let result = LGTeacher().testMirror()
print(result) //["age": 18]
添加Error
優化當前代碼的錯誤信息
class LGTeacher {
var age = 18
}
enum JsonMapError: Error {
case emptyKey
case notComfirmProtocol
}
protocol JsonMap {
func testMirror() throws -> Any
}
extension JsonMap {
func testMirror() throws -> Any {
let mirror = Mirror(reflecting: self)
guard !mirror.children.isEmpty else {
return self
}
var dic = [String:Any]()
for child in mirror.children {
if let value = child.value as? JsonMap {
if let key = child.label {
dic[key] = try value.testMirror()
}else {
throw JsonMapError.emptyKey
}
}else {
throw JsonMapError.notComfirmProtocol
}
}
return dic
}
}
extension LGTeacher: JsonMap{}
extension Int: JsonMap {}
let result = try? LGTeacher().testMirror()
print(result) //Optional(["age": 18])
關于rethrows
rethrows
本身并不拋出異常或處理異常,只是起到了傳遞異常的作用。通常在閉包中使用
enum ClosureError: Error {
case lessZero
}
func test(closure: (Int) throws -> Int, num: Int) rethrows -> Int {
try closure(num)
}
do {
let result = try test(closure: {
if $0 < 0 {
throw ClosureError.lessZero
}else {
return $0 + 10
}
}, num: 1)
print(result)
}catch {
print(error) //lessZero
}
3.Mirror源碼解析
進入源文件搜索Mirror.swift
,忽略掉一些細節,在源碼中找到初始化方法
public init(reflecting subject: Any) {
if case let customized as CustomReflectable = subject {
self = customized.customMirror
} else {
self = Mirror(internalReflecting: subject)
}
}
- 如果
subject
遵循了CustomReflectable
協議self = customized.customMirror
- 否則
self
等于內部的反射初始化函數生成的實例
1.關于CustomReflectable
class LGTeacher: CustomReflectable {
var age = 18
var name = "Kody"
var customMirror: Mirror {
//KeyValuePairs,鍵值對
let info = KeyValuePairs<String,Any>(dictionaryLiteral: ("currentAge",age), ("currentName", name))
let mirror = Mirror.init(self, children: info, displayStyle: .class, ancestorRepresentation: .generated)
return mirror
}
}
let mirror = Mirror(reflecting: LGTeacher())
for child in mirror.children {
if let label = child.label {
print("\(label):\(child.value)")
}
}
//此時打印的值,就是我們在customMirror中聲明的KeyValuePairs
//currentAge:18
//currentName:Kody
//通過LLDB調試時,也能反射出當前的調試信息
//(lldb) po LGTeacher()
//? <LGTeacher: 0x10b156b70>
// - currentAge : 18
// - currentName : "Kody"
2.關于Mirror
的內部初始化方法Mirror(internalReflection:subject))
extension Mirror {
internal init(internalReflecting subject: Any,
subjectType: Any.Type? = nil,
customAncestor: Mirror? = nil)
{
//獲取傳進來的subject的類型信息。使用type(of: T)獲取真實信息
let subjectType = subjectType ?? _getNormalizedType(subject, type: type(of: subject))
//獲取屬性信息
let childCount = _getChildCount(subject, type: subjectType)
let children = (0 ..< childCount).lazy.map({
getChild(of: subject, type: subjectType, index: $0)
})
self.children = Children(children)
...
}
關于_getNormalizedType
和_getChildCount
函數
@_silgen_name("swift_reflectionMirror_normalizedType")
internal func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
@_silgen_name("swift_reflectionMirror_count")
internal func _getChildCount<T>(_: T, type: Any.Type) -> Int
-
@_silgen_name
是Swift
的一個隱藏符號
,作用是將某個C/C++
語言函數直接映射為Swift函數
3.關于Swift
調用C語言
函數
傳統思路
1.創建C文件,.h(方法的聲明)和.c(方法的實現)文件
//testC.h
int sum(int a, int b);
//testC.c
int sum(int a, int b) {
return a + b;
}
2.創建橋接文件,import我們創建的.h文件
//一般在導入C/C++文件時使用include,導入OC文件使用improt
#include "testC.h"
3.在Swift代碼中使用
使用@_silgen_name
1.第一步不變
2.使用_silgen_name直接將C函數映射成Swift函數
@_silgen_name("sum")
internal func my_sum(a: Int, b: Int) -> Int
let result = my_sum(a: 10, b: 10)
print(result) //20
訪問控制(Access Control)
-
open
可以在任意地方被訪問、繼承和重寫 -
public
可以在任意地方被訪問,在其它模塊(module)內不能被繼承、重寫 -
internal
默認。在整個模塊內可以訪問、繼承和重寫 -
fileprivaty
在同一個文件內(.swift)可以被訪問、繼承和重寫。修飾的函數靜態派發
-
privaty
私有的。只能在自己的類中訪問。修飾的函數靜態派發
-
final
只能修飾class。意味著該類是最終類,不能被繼承。修飾的class中的函數派發方式為靜態派發
4.找到C++代碼swift_reflectionMirror_normalizedType
// func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
const Metadata *swift_reflectionMirror_normalizedType(OpaqueValue *value,
const Metadata *type,
const Metadata *T) {
return call(value, T, type, [](ReflectionMirrorImpl *impl) { return impl->type; });
}
-
call
調用了一個函數,通過回調函數impl->type
,返回type
的信息 - 通過注釋可以得出
Any.Type
就是Metadata
5.call
的實現
template<typename F>
auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedType,
const F &f) -> decltype(f(nullptr))
{
const Metadata *type;
OpaqueValue *value;
std::tie(type, value) = unwrapExistential(T, passedValue);
if (passedType != nullptr) {
type = passedType;
}
//如果不是Class,執行這個call回調函數
auto call = [&](ReflectionMirrorImpl *impl) {
impl->type = type;
impl->value = value;
auto result = f(impl);
return result;
};
//如果是Class,執行callClass回調函數
auto callClass = [&] {
if (passedType == nullptr) {
// Get the runtime type of the object.
const void *obj = *reinterpret_cast<const void * const *>(value);
auto isa = _swift_getClass(obj);
// Look through artificial subclasses.
while (isa->isTypeMetadata() && isa->isArtificialSubclass()) {
isa = isa->Superclass;
}
passedType = isa;
}
#if SWIFT_OBJC_INTEROP
// If this is a pure ObjC class, reflect it using ObjC's runtime facilities.
// ForeignClass (e.g. CF classes) manifests as a NULL class object.
auto *classObject = passedType->getClassObject();
if (classObject == nullptr || !classObject->isTypeMetadata()) {
ObjCClassImpl impl;
return call(&impl);
}
#endif
// Otherwise, use the native Swift facilities.
ClassImpl impl;
return call(&impl);
};
//通過MetadataKind來判斷類型,執行不同的處理
switch (type->getKind()) {
case MetadataKind::Tuple: {
TupleImpl impl;
return call(&impl);
}
case MetadataKind::Struct: {
StructImpl impl;
return call(&impl);
}
case MetadataKind::Enum:
case MetadataKind::Optional: {
EnumImpl impl;
return call(&impl);
}
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass:
case MetadataKind::Class: {
return callClass();
}
case MetadataKind::Metatype:
case MetadataKind::ExistentialMetatype: {
MetatypeImpl impl;
return call(&impl);
}
...
}
- 關于
Call
函數,聲明了2個回調函數來獲取Class
和非Class
的type - 這2個回調函數都有一個參數
ReflectionMirrorImpl
- 根據不同的
MetadataKind
有不同的Impl
,例如枚舉EnumImpl
6.關于ReflectionMirrorImpl
// Abstract base class for reflection implementations.
struct ReflectionMirrorImpl {
const Metadata *type;
OpaqueValue *value;
virtual char displayStyle() = 0;
virtual intptr_t count() = 0;
virtual intptr_t childOffset(intptr_t index) = 0;
virtual const FieldType childMetadata(intptr_t index,
const char **outName,
void (**outFreeFunc)(const char *)) = 0;
virtual AnyReturn subscript(intptr_t index, const char **outName,
void (**outFreeFunc)(const char *)) = 0;
virtual const char *enumCaseName() { return nullptr; }
#if SWIFT_OBJC_INTEROP
virtual id quickLookObject() { return nil; }
#endif
// For class types, traverse through superclasses when providing field
// information. The base implementations call through to their local-only
// counterparts.
virtual intptr_t recursiveCount() {
return count();
}
virtual intptr_t recursiveChildOffset(intptr_t index) {
return childOffset(index);
}
virtual const FieldType recursiveChildMetadata(intptr_t index,
const char **outName,
void (**outFreeFunc)(const char *))
{
return childMetadata(index, outName, outFreeFunc);
}
virtual ~ReflectionMirrorImpl() {}
};
-
ReflectionMirrorImpl
反射實現的抽象基類
7.關于EnumImpl
// Implementation for enums.
struct EnumImpl : ReflectionMirrorImpl {
//是否能反射
bool isReflectable() {
//做一個metadata的強轉
const auto *Enum = static_cast<const EnumMetadata *>(type);
//找到metadata的Description
const auto &Description = Enum->getDescription();
//根據Description中的isReflectable字段來判斷是否可以反射
return Description->isReflectable();
}
const char *getInfo(unsigned *tagPtr = nullptr,
const Metadata **payloadTypePtr = nullptr,
bool *indirectPtr = nullptr) {
// 'tag' is in the range [0..NumElements-1].
unsigned tag = type->vw_getEnumTag(value);
StringRef name;
FieldType info;
//獲取FieldDescriptor的信息,也就是屬性信息存放的地方
std::tie(name, info) = getFieldAt(type, tag);
const Metadata *payloadType = info.getType();
bool indirect = info.isIndirect();
if (tagPtr)
*tagPtr = tag;
if (payloadTypePtr)
*payloadTypePtr = payloadType;
if (indirectPtr)
*indirectPtr = indirect;
return name.data();
}
char displayStyle() override {
return 'e';
}
//獲取count
intptr_t count() override {
if (!isReflectable()) {
return 0;
}
// No fields if reflecting the enumeration type instead of a case
if (!value) {
return 0;
}
const Metadata *payloadType;
//獲取掛載類型,也就是Metadata
getInfo(nullptr, &payloadType, nullptr);
return (payloadType != nullptr) ? 1 : 0;
}
...
}
8.關于getFieldAt
static std::pair<StringRef /*name*/, FieldType /*fieldInfo*/>
getFieldAt(const Metadata *base, unsigned index) {
using namespace reflection;
// If we failed to find the field descriptor metadata for the type, fall
// back to returning an empty tuple as a standin.
auto failedToFindMetadata = [&]() -> std::pair<StringRef, FieldType> {
auto typeName = swift_getTypeName(base, /*qualified*/ true);
missing_reflection_metadata_warning(
"warning: the Swift runtime found no field metadata for "
"type '%*s' that claims to be reflectable. Its fields will show up as "
"'unknown' in Mirrors\n",
(int)typeName.length, typeName.data);
return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))};
};
//獲取TargetxxxDescriptor信息
auto *baseDesc = base->getTypeContextDescriptor();
if (!baseDesc)
return failedToFindMetadata();
//獲取descriptor里的FieldDescriptor的信息
auto *fields = baseDesc->Fields.get();
if (!fields)
return failedToFindMetadata();
auto &field = fields->getFields()[index];
// Bounds are always valid as the offset is constant.
//獲取屬性的名稱
auto name = field.getFieldName();
...
}
- 此時的邏輯和Swift -- 3.屬性通過
Mach-O
找到我們的屬性名稱是一致的
Mirror
的工作原理:可以看出Mirro
是通過Metadata(當前類型的元數據)
、getDescription(當前類型的描述)
、FieldDescription(當前類型屬性的描述)
來實現的。
四.結合源碼還原TargetEnumMetaData
在Swift -- 1.類與結構體(上)探究了TargetClassMetadata
的數據類型,在尋找的過程中也遇到了TargetEnumMetaData
,因此結合源碼還原一下TargetEnumMetaData
1.關于MetadataKind
之前我們總結了一張關于MetadataKind
的表
name | value |
---|---|
Class | 0x0 |
Struct | 0x200 |
Enum | 0x201 |
Optional | 0x202 |
ForeignClass | 0x203 |
Opaque | 0x300 |
Tuple | 0x301 |
Function | 0x302 |
Existential | 0x303 |
Metatype | 0x304 |
ObjCClassWrapper | 0x305 |
ExistentialMetatype | 0x306 |
HeapLocalVariable | 0x400 |
HeapGenericLocalVariable | 0x500 |
ErrorObject | 0x501 |
LastEnumerated | 0x7FF |
那么這些值是怎么的來的呢?接下來通過源碼分析一下
-
kind
的內存大小
//1.在TargetMetaData中找到kind
/// The kind. Only valid for non-class metadata; getKind() must be used to get
/// the kind value.
StoredPointer Kind;
//2.關于StoredPointer
using StoredPointer = typename Runtime::StoredPointer;
//3.關于Runtime
using Runtime = External<RuntimeTarget<sizeof(uintptr_t)>>;
//4.關于uintptr_t
typedef unsigned long uintptr_t;
- 得出
kind
大小為Int64
2.驗證kind
值
找到MetadataKind
/// Non-type metadata kinds have this bit set.
//沒有類型的元數據的kind存放在此位
const unsigned MetadataKindIsNonType = 0x400;
/// Non-heap metadata kinds have this bit set.
//不是堆元數據存放在此位。也就是除了引用類型
const unsigned MetadataKindIsNonHeap = 0x200;
//運行時私有元數據已設置此位
const unsigned MetadataKindIsRuntimePrivate = 0x100;
/// Kinds of Swift metadata records. Some of these are types, some
/// aren't.
enum class MetadataKind : uint32_t {
#define METADATAKIND(name, value) name = value,
#define ABSTRACTMETADATAKIND(name, start, end) \
name##_Start = start, name##_End = end,
#include "MetadataKind.def"
/// The largest possible non-isa-pointer metadata kind value.
///
/// This is included in the enumeration to prevent against attempts to
/// exhaustively match metadata kinds. Future Swift runtimes or compilers
/// may introduce new metadata kinds, so for forward compatibility, the
/// runtime must tolerate metadata with unknown kinds.
/// This specific value is not mapped to a valid metadata kind at this time,
/// however.
LastEnumerated = 0x7FF,
};
//關于MetadataKind.def
/// A class type.
NOMINALTYPEMETADATAKIND(Class, 0)
/// A struct type.
NOMINALTYPEMETADATAKIND(Struct, 0 | MetadataKindIsNonHeap)
/// An enum type.
/// If we add reference enums, that needs to go here.
NOMINALTYPEMETADATAKIND(Enum, 1 | MetadataKindIsNonHeap)
/// An optional type.
NOMINALTYPEMETADATAKIND(Optional, 2 | MetadataKindIsNonHeap)
/// A foreign class, such as a Core Foundation class.
METADATAKIND(ForeignClass, 3 | MetadataKindIsNonHeap)
/// A type whose value is not exposed in the metadata system.
METADATAKIND(Opaque, 0 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// A tuple.
METADATAKIND(Tuple, 1 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// A monomorphic function.
METADATAKIND(Function, 2 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// An existential type.
METADATAKIND(Existential, 3 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// A metatype.
METADATAKIND(Metatype, 4 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// An ObjC class wrapper.
METADATAKIND(ObjCClassWrapper, 5 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// An existential metatype.
METADATAKIND(ExistentialMetatype, 6 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// A heap-allocated local variable using statically-generated metadata.
METADATAKIND(HeapLocalVariable, 0 | MetadataKindIsNonType)
/// A heap-allocated local variable using runtime-instantiated metadata.
METADATAKIND(HeapGenericLocalVariable,
0 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
/// A native error object.
METADATAKIND(ErrorObject,
1 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
/// A heap-allocated task.
METADATAKIND(Task,
2 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
/// A non-task async job.
METADATAKIND(Job,
3 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
2.通過源碼還原TargetEnumMetadata
1.進入TargetEnumMetadata
,里面并沒有結構信息
2.進入TargetEnumMetadata
父類TargetValueMetadata
/// An out-of-line description of the type.
TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;
- 存在一個
Description
描述信息
3.進入TargetValueMetadata
父類TargetMetadata
(最終基類)
StoredPointer Kind;
4.此時我們就能確定TargetEnumMetadata
的外層結構結構
struct TargetEnumMetadata {
var kind: Int
var descriptor: UnsafePointer<TargetEnumDescriptor>
}
struct TargetEnumDescriptor {
}
5.進入TargetEnumDescriptor
/// The number of non-empty cases in the enum are in the low 24 bits;
/// the offset of the payload size in the metadata record in words,
/// if any, is stored in the high 8 bits.
uint32_t NumPayloadCasesAndPayloadSizeOffset;
/// The number of empty cases in the enum.
uint32_t NumEmptyCases;
-
NumPayloadCasesAndPayloadSizeOffset
具有掛載的case數量 -
NumEmptyCases
沒有掛載的case數量
6.進入TargetEnumDescriptor
父類TargetValueTypeDescriptor
template <typename Runtime>
class TargetTypeContextDescriptor
: public TargetContextDescriptor<Runtime> {
public:
/// The name of the type.
TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name;
/// A pointer to the metadata access function for this type.
///
/// The function type here is a stand-in. You should use getAccessFunction()
/// to wrap the function pointer in an accessor that uses the proper calling
/// convention for a given number of arguments.
TargetRelativeDirectPointer<Runtime, MetadataResponse(...),
/*Nullable*/ true> AccessFunctionPtr;
/// A pointer to the field descriptor for the type, if any.
TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
/*nullable*/ true> Fields;
...
}
- 存有
Name
、AccessFunctionPtr
、Fields
3個變量,并且類型都是TargetRelativeDirectPointer
7.進入TargetTypeContextDescriptor
父類TargetContextDescriptor
(最終基類)
/// Base class for all context descriptors.
template<typename Runtime>
struct TargetContextDescriptor {
/// Flags describing the context, including its kind and format version.
ContextDescriptorFlags Flags;
/// The parent context, or null if this is a top-level context.
TargetRelativeContextPointer<Runtime> Parent;
...
}
using RelativeContextPointerIntPair =
RelativeIndirectablePointerIntPair<const Context<Runtime>, IntTy,
/*nullable*/ true, int32_t,
TargetSignedContextPointer<Runtime, Context>>;
-
Flags
大小為UInt32 -
Parent
類型為TargetRelativeContextPointer
8.總結一下TargetEnumMetadata
數據結構
9.關于TargetRelativeDirectPointer
和TargetRelativeContextPointer
相對地址的指針
using TargetRelativeDirectPointer
= typename Runtime::template RelativeDirectPointer<Pointee, Nullable>;
using TargetRelativeContextPointer =
RelativeIndirectablePointer<const Context<Runtime>,
/*nullable*/ true, int32_t,
TargetSignedContextPointer<Runtime, Context>>;
/// A direct relative reference to an object that is not a function pointer.
template <typename T, bool Nullable, typename Offset>
class RelativeDirectPointer<T, Nullable, Offset,
typename std::enable_if<!std::is_function<T>::value>::type>
: private RelativeDirectPointerImpl<T, Nullable, Offset>
{
using super = RelativeDirectPointerImpl<T, Nullable, Offset>;
public:
using super::get;
using super::super;
RelativeDirectPointer &operator=(T *absolute) & {
super::operator=(absolute);
return *this;
}
operator typename super::PointerTy() const & {
return this->get();
}
const typename super::ValueTy *operator->() const & {
return this->get();
}
using super::isNull;
};
類似于我們在
Mach-o
中尋找屬性時所看到的很多偏移量,在Swift
中大量使用相對地址指針直接尋址,比如我們定義一個
var a = 10
,那么通過a的指針地址0x1000
拿到變量10在內存中的地址(比如是0x1004
)。此時a通過2步拿到了內存中的10相對尋址,比如我們定義一個
var a = 10
,那么a指針地址0x1000存放的是offset,這個offset代表著10在內存中的地址(0x1004
)與當前a地址的偏移量,也就是4。這樣就可以通過offset來獲取值。節省大量的地址信息,優化內存空間
翻譯為Swift
中的相對指針
struct TargetRelativeDirectPointer<Pointee> {
var offset: Int32
mutating func getMeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
let offset = self.offset
return withUnsafePointer(to: &self) { p in
return UnsafeMutableRawPointer(mutating: p.advanced(by: numericCast(offset))).assumingMemoryBound(to: Pointee.self)
}
}
}
10.關于mangledTypeName
的還原
使用C語言標準庫
函數swift_getTypeByMangledNameInContext
//方式1:.h文件聲明
/*
typeNameStart,混淆名稱的指針地址
typeNameLength,混淆名稱的長度
context,上下文,Descriptor的指針地址
genericArgs,如果有泛型參數,泛型參數的指針地址。參考HandyJson,metadata地址再偏移2個原生指針的大小(2個8字節)
*/
const void * _Nullable swift_getTypeByMangledNameInContext(
const void * _Nullable typeNameStart,
int typeNameLength,
const void * _Nullable context,
const void * _Nullable const * _Nullable genericArgs);
//這里使用編譯器字段@_silgen_name()方式
@_silgen_name("swift_getTypeByMangledNameInContext")
private func getTypeByMangledNameInContext(
mangledNamePtr: UnsafePointer<UInt8>,
mangledNameLength: Int,
genericContext: UnsafeRawPointer?,
genericArgs: UnsafeRawPointer?) -> Any.Type?
func getTypeByMangleName(
mangledNamePtr: UnsafePointer<UInt8>,
genericContext: UnsafeRawPointer?, metadata: UnsafeRawPointer) -> Any.Type? {
return getTypeByMangledNameInContext(mangledNamePtr: mangledNamePtr,
mangledNameLength: getMangledTypeNameSize(mangledNamePtr),
genericContext: UnsafeRawPointer(genericContext),
genericArgs: getGenericArgs(metadata))
}
//參考HandyJson,暫時沒找到測量mangledName的長度
func getMangledTypeNameSize(_ managedName: UnsafePointer<UInt8>) -> Int {
// TODO: should find the actually size
return 256
}
//獲取泛型參數在metadata中的偏移量
func getGenericArgumentOffset() -> Int {
return 2
}
//如果有泛型參數,需要傳入的泛型參數指針。參考HandyJson
func getGenericArgs(_ metadata: UnsafeRawPointer) -> UnsafeRawPointer? {
let offSetPtr = metadata.advanced(by: getGenericArgumentOffset() * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Int.self)
if offSetPtr.pointee == 0 {
return nil
}
return UnsafeRawPointer(offSetPtr)
}
11.還原TargetEnumMetadata
TangledTypeName.swift
@_silgen_name("swift_getTypeByMangledNameInContext")
private func getTypeByMangledNameInContext(
mangledNamePtr: UnsafePointer<UInt8>,
mangledNameLength: Int,
genericContext: UnsafeRawPointer?,
genericArgs: UnsafeRawPointer?) -> Any.Type?
func getTypeByMangleName(
mangledNamePtr: UnsafePointer<UInt8>,
genericContext: UnsafeRawPointer?) -> Any.Type? {
return getTypeByMangledNameInContext(mangledNamePtr: mangledNamePtr, mangledNameLength: getMangledTypeNameSize(mangleTypeNamePtr), genericContext: UnsafeRawPointer(genericContext), genericArgs: getGenericArgs(mangledNamePtr))
}
//參考HandyJson,暫時沒找到測量mangledName的長度
func getMangledTypeNameSize(_ managedName: UnsafePointer<UInt8>) -> Int {
// TODO: should find the actually size
return 256
}
//獲取泛型參數在Descriptor中的偏移量
func getGenericArgumentOffset() -> Int {
return 2
}
//如果有泛型參數,需要傳入的泛型參數指針
func getGenericArgs(_ descriptor: UnsafeRawPointer) -> UnsafeRawPointer {
return UnsafeRawPointer(descriptor.advanced(by: getGenericArgumentOffset() * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self))
}
TargetMetadata.swift
struct TargetEnumMetadata{
var kind: Int
var descriptor: UnsafeMutablePointer<TargetEnumDescriptor>
}
struct TargetEnumDescriptor {
var flags: UInt32
var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
var name: TargetRelativeDirectPointer<CChar>
var accessFunctionPtr: TargetRelativeDirectPointer<UnsafeRawPointer>
var fields: TargetRelativeDirectPointer<FieldDescriptor>
var numPayloadCaseAndPayloadSizeOffset: UInt32
var numEmptyCases: UInt32
}
struct FieldDescriptor {
var mangledTypeName: TargetRelativeDirectPointer<UInt8>
var superClass: TargetRelativeDirectPointer<CChar>
var kind: UInt16
var fieldRecordSize: UInt16
var numField: UInt32
//連續的內存空間存放我們的var變量/case的名稱
//UnsafeBufferPointer,連續的內存空間指針
var fieldRecords: FieldRecordBuffer<FieldRecord>
/*
kind: FieldDescriptorKind(枚舉值依次遞增)
Struct: 0x1
Class
Enum
MultiPayloadEnum
Protocol
ClassProtocol
ObjCProtocol
ObjCClass
*/
}
struct FieldRecordBuffer<Element> {
var element: Element
//方式1.返回buffer連續內存空間
mutating func buffer(count: Int) -> UnsafeMutableBufferPointer<Element> {
return withUnsafePointer(to: &self) {
return UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(start: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self), count: count))
}
}
//方式2.根據下標獲取數組元素
mutating func getFieldRecord(index: Int) -> UnsafeMutablePointer<Element> {
return withUnsafePointer(to: &self) {
return UnsafeMutablePointer(mutating: UnsafeRawPointer($0.advanced(by: index)).assumingMemoryBound(to: Element.self))
}
}
}
struct FieldRecord {
var flags: UInt32
var mangledTypeName: TargetRelativeDirectPointer<UInt8>
var fieldName: TargetRelativeDirectPointer<CChar>
/*
flags: FieldRecordFlags
IsIndirectCase = 0x1,
IsVar = 0x2,
IsArtificial = 0x4,
其它情況 = 0
*/
}
struct TargetRelativeDirectPointer<Pointee> {
var offset: Int32
mutating func getMeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
let offset = self.offset
return withUnsafePointer(to: &self) {
/*
注意:這里的$0必須轉化為UnsafeRawPointer再執行advanced
如果類型指針advanced會出問題。
原始是:在介紹指針的時候說過,如果是類型指針的話,
advanced(x)相當于移動該類型的內存大小*x。
*/
return UnsafeMutableRawPointer(mutating: UnsafeRawPointer($0).advanced(by: numericCast(offset))).assumingMemoryBound(to: Pointee.self)
}
}
}
main.swift
enum Shape {
case circle(radious:Int)
case rectangle(Bool)
case triangle
}
let ptr = unsafeBitCast(Shape.self as Any.Type, to: UnsafeMutablePointer<TargetEnumMetadata>.self)
//錯誤寫法
//var descriptor = ptr.pointee.descriptor.pointee
//let namePtr = descriptor.name.getMeasureRelativeOffset()
//這里只能這樣寫,值類型賦值會拷貝。執行到getMeasureRelativeOffset會出問題
let namePtr = ptr.pointee.descriptor.pointee.name.getMeasureRelativeOffset()
print("enum kind: ", "0x\(String(ptr.pointee.kind, radix: 16))") //0x201對應Kind表為Enum
//枚舉的名稱
print("enum name", String(cString: namePtr)) // Shape
//枚舉的帶有掛載的數量
print("enum numPayloadCaseAndPayloadSizeOffset: ", ptr.pointee.descriptor.pointee.numPayloadCaseAndPayloadSizeOffset) // 1
//枚舉的沒有帶掛載的數量
print("enum numEmptyCases: ", ptr.pointee.descriptor.pointee.numEmptyCases) // 2
let fieldPtr = ptr.pointee.descriptor.pointee.fields.getMeasureRelativeOffset()
//1.關于還原混淆后的TypeName
let mangleTypeNamePtr = fieldPtr.pointee.mangledTypeName.getMeasureRelativeOffset()
let nameType = getTypeByMangleName(mangledNamePtr: mangleTypeNamePtr, genericContext: UnsafeRawPointer(ptr.pointee.descriptor), metadata: ptr)
print("type Name", nameType as Any)
//2.superClass
let superClassPtr = fieldPtr.pointee.superClass.getMeasureRelativeOffset()
print("enum superClass: ", String(cString: superClassPtr)) //枚舉并沒有superClass
//3.kind
print("enum field kind: ", fieldPtr.pointee.kind)
//4.fieldRecordSize
print("enum fieldRecordSize: ",fieldPtr.pointee.fieldRecordSize)
//5.numField
print("enum numField: ",fieldPtr.pointee.numField)
//6.FieldRecod
//方式1:使用Buffer的方式
let bufferPtr = fieldPtr.pointee.fieldRecords.buffer(count: numericCast(fieldPtr.pointee.numField))
for i in 0..<bufferPtr.count {
print("type:\(getTypeByMangleName(mangledNamePtr: bufferPtr[i].mangledTypeName.getMeasureRelativeOffset(), genericContext: ptr.pointee.descriptor, metadata: ptr) as Any) case值:\(String(cString: bufferPtr[i].fieldName.getMeasureRelativeOffset()))")
}
//方式2:使用指針位移的方式
//for i in 0..<fieldPtr.pointee.numField {
// let filedRecord = fieldPtr.pointee.fieldRecords.getFieldRecord(index: numericCast(i))
// //這里的flags為0
//
// print("type:\(getTypeByMangleName(mangledNamePtr: filedRecord.pointee.mangledTypeName.getMeasureRelativeOffset(), genericContext: ptr.pointee.descriptor) as Any) case值:\(String(cString: filedRecord.pointee.fieldName.getMeasureRelativeOffset()))")
//}
打印信息
enum kind: 0x201
enum name Shape
enum numPayloadCaseAndPayloadSizeOffset: 2
enum numEmptyCases: 1
type Name Optional(swiftTest.Shape)
enum superClass:
enum field kind: 3
enum fieldRecordSize: 12
enum numField: 3
type:Optional((radious: Swift.Int)) case值:circle
type:Optional(Swift.Bool) case值:rectangle
type:nil case值:triangle
五.參考HandyJson總結TargetClassMetadata
在Swift -- 4.指針&內存管理通過指針獲取了類名
、屬性名稱
、、vtable
及分析了vtable中的函數名稱存放位置(符號表及字符串表)
接下來我們需要獲取到的是屬性類型
、屬性值
fieldOffsetVectorOffset
可以理解為屬性信息基于metadata
的偏移量(value*8字節)。連續的內存空間,每8字節存放屬性值內存地址基于metadata
的偏移量
可以理解為取出屬性值需要拿到2次的offSet
,根據內存地址然后獲取到最終的值
TargetMetadata.swift
struct TargetClassMetadata{
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutablePointer<TargetClassDescriptor>
var iVarDestroyer: UnsafeRawPointer
}
struct TargetClassDescriptor{
var flags: UInt32
var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
var name: TargetRelativeDirectPointer<CChar>
var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>
var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
var superClassType: TargetRelativeDirectPointer<CChar>
var metadataNegativeSizeInWords: UInt32
var metadataPositiveSizeInWords: UInt32
var numImmediateMembers: UInt32
var numFields: UInt32
var fieldOffsetVectorOffset: UInt32
var Offset: UInt32
//VTable數量
var size: UInt32
//VTable
var firstVtableData: VTableBuffer<VTable>
}
struct VTableBuffer<Element> {
var element: Element
mutating func buffer(of count: Int) -> UnsafeMutableBufferPointer<Element> {
return withUnsafePointer(to: &self) {
return UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(start: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self), count: count))
}
}
}
struct VTable {
var flags: TargetMethodDescriptor
//注意此時的相對地址指針的偏移量不能使用Int,因為符號位會影響offset
var methodImp: TargetRelativeDirectPointerOffsetUint<UnsafeRawPointer>
}
struct TargetMethodDescriptor {
var flagType: UInt32
//從源碼恢復的
enum KindType: UInt32 {
case Method,
Init,
Getter,
Setter,
ModifyCoroutine,
ReadCoroutine
}
enum FlagTypeMask: UInt32 {
case KindMask = 0x0F, // 16 kinds should be enough for anybody
IsInstanceMask = 0x10,
IsDynamicMask = 0x20,
IsAsyncMask = 0x40,
// ExtraDiscriminatorShift = 16, //這個16與0x10沖突了
ExtraDiscriminatorMask = 0xFFFF0000
}
func kindType() -> KindType {
return KindType(rawValue: flagType & FlagTypeMask.KindMask.rawValue)!
}
}
struct FieldDescriptor {
var mangledTypeName: TargetRelativeDirectPointer<UInt8>
var superClass: TargetRelativeDirectPointer<CChar>
var kind: UInt16
var fieldRecordSize: UInt16
var numField: UInt32
//連續的內存空間存放我們的var變量/case的名稱
//UnsafeBufferPointer,連續的內存空間指針
var fieldRecords: FieldRecordBuffer<FieldRecord>
/*
kind: FieldDescriptorKind(枚舉值依次遞增)
Struct: 0x1
Class
Enum
MultiPayloadEnum
Protocol
ClassProtocol
ObjCProtocol
ObjCClass
*/
}
struct FieldRecordBuffer<Element> {
var element: Element
//方式1.返回buffer連續內存空間
mutating func buffer(count: Int) -> UnsafeMutableBufferPointer<Element> {
return withUnsafePointer(to: &self) {
return UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(start: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self), count: count))
}
}
//方式2.根據下標獲取數組元素
mutating func getFieldRecord(index: Int) -> UnsafeMutablePointer<Element> {
return withUnsafePointer(to: &self) {
return UnsafeMutablePointer(mutating: UnsafeRawPointer($0.advanced(by: index)).assumingMemoryBound(to: Element.self))
}
}
}
struct FieldRecord {
var flags: UInt32
var mangledTypeName: TargetRelativeDirectPointer<UInt8>
var fieldName: TargetRelativeDirectPointer<CChar>
/*
flags: FieldRecordFlags
IsIndirectCase = 0x1,
IsVar = 0x2,
IsArtificial = 0x4,
其它情況 = 0
*/
}
struct TargetRelativeDirectPointer<Pointee> {
var offset: Int32
mutating func getMeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
let offset = self.offset
return withUnsafePointer(to: &self) {
/*
注意:這里的$0必須轉化為UnsafeRawPointer再執行advanced
如果類型指針advanced會出問題。
原始是:在介紹指針的時候說過,如果是類型指針的話,
advanced(x)相當于移動該類型的內存大小*x。
*/
return UnsafeMutableRawPointer(mutating: UnsafeRawPointer($0).advanced(by: numericCast(offset))).assumingMemoryBound(to: Pointee.self)
}
}
}
struct TargetRelativeDirectPointerOffsetUint<Pointee> {
var offset: UInt32
mutating func getMeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
let offset = self.offset
return withUnsafePointer(to: &self) {
/*
注意:這里的$0必須轉化為UnsafeRawPointer再執行advanced
如果類型指針advanced會出問題。
原始是:在介紹指針的時候說過,如果是類型指針的話,
advanced(x)相當于移動該類型的內存大小*x。
*/
return UnsafeMutableRawPointer(mutating: UnsafeRawPointer($0).advanced(by: numericCast(offset))).assumingMemoryBound(to: Pointee.self)
}
}
}
main.swift
class LGTeacher {
var age = 18
var name = "Kody"
func teach() {
print("teach")
}
func teach1() {
print("teach1")
}
func teach2() {
print("teach2")
}
}
let ptr = unsafeBitCast(LGTeacher.self as Any.Type, to: UnsafeMutablePointer<TargetClassMetadata>.self)
let descriptorPtr = ptr.pointee.typeDescriptor
//1.獲取類的名稱
print("Class Name:", String(cString: descriptorPtr.pointee.name.getMeasureRelativeOffset()))
//2.獲取類的類型
let fieldDescriptorPtr = descriptorPtr.pointee.fieldDescriptor.getMeasureRelativeOffset()
print("Class type:", getTypeByMangleName(mangledNamePtr: fieldDescriptorPtr.pointee.mangledTypeName.getMeasureRelativeOffset(), genericContext: descriptorPtr, metadata: ptr) as Any)
//2.獲取屬性名稱
let fieldRecordBuffer = fieldDescriptorPtr.pointee.fieldRecords.buffer(count: numericCast(fieldDescriptorPtr.pointee.numField))
let propertyPtr = UnsafeRawPointer(ptr).advanced(by: numericCast(descriptorPtr.pointee.fieldOffsetVectorOffset))
let t = LGTeacher()
//3.獲取t的metadata地址
let tMetadataPtr = Unmanaged.passUnretained(t).toOpaque()
//4.獲取屬性值的信息
//獲取屬性值(offset)存放的地址(基于metadata偏移fieldOffsetVectorOffset個8字節,每8字節存放1個屬性的偏移信息)
let offsets = UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self).advanced(by: numericCast(descriptorPtr.pointee.fieldOffsetVectorOffset))
protocol AnyExtensions {}
extension AnyExtensions {
//取數據
//這里的self指的是當前類型
static func value(_ pointer: UnsafeRawPointer) -> Any {
return pointer.assumingMemoryBound(to: self).pointee
}
}
struct TargetProtocolMetadata {
var type: Any.Type
//協議見證表,后續寫到協議相關的再談
var witness: Int
}
for i in 0..<fieldRecordBuffer.count {
let type = getTypeByMangleName(mangledNamePtr: fieldRecordBuffer[i].mangledTypeName.getMeasureRelativeOffset(), genericContext: descriptorPtr, metadata: ptr)!
let fieldName = String(cString: fieldRecordBuffer[i].fieldName.getMeasureRelativeOffset())
//獲取屬性偏移信息
let valueOffset = offsets[i]
/*
屬性在內存中的實際地址
其實如果能夠聯想到前面的metadata,
此時的2個屬性肯定是放在heapObject(16字節)后
大膽可以猜測一下這里的偏移量一個是16一個是24
*/
let valuePtr = tMetadataPtr.advanced(by: valueOffset)
//疑問:現在有了type怎么取出不同的值?
/*
方式1:根據HandyJson源碼,利用協議里self/Self獲取實際類型的特性。
將聲明的Extensions的metadata更換為我們得知的metadata,利用協議方法內self/Self能夠獲取真實類型的特性,完成取值操作。
*/
//獲取結構體的元數據,靜態類型為AnyExtensions,動態類型為Extensions
struct Extensions: AnyExtensions {}
var extensions: AnyExtensions.Type = Extensions.self
//拿到extension的地址更換metadata
withUnsafePointer(to: &extensions) {
UnsafeMutableRawPointer(mutating: $0).assumingMemoryBound(to: Any.Type.self).pointee = type
}
//取值
// let value = extensions.value(valuePtr)
// print("\(fieldName)->\(type)->\(extensions.value(valuePtr))")
/*
方式2:將自定義的結構體(已經是新的metadata)按位轉化為Protocol.Type實現對maetadata的更換。
這里的TargetProtocolMetadata指的是通過源碼總結出的metadata數據結構
一樣的原理,也是通過更換了metadata來實現不同數據的取值,這里不同的是轉化前metadata就已經是修改后的。
*/
let protocolMetaData = TargetProtocolMetadata(type: type, witness: 0)
let protocolType = unsafeBitCast(protocolMetaData, to: AnyExtensions.Type.self)
print("\(fieldName)->\(type)->\(protocolType.value(valuePtr))")
}
//3.獲取V-Table,及執行函數方法
let vtableBuffer = descriptorPtr.pointee.firstVtableData.buffer(of: numericCast(descriptorPtr.pointee.size))
typealias Function = @convention(c) ()-> Void
var vmAddressSize = getsegbyname("__PAGEZERO").pointee.vmsize
for i in 0..<vtableBuffer.count {
if vtableBuffer[i].flags.kindType() == .Method {
let impOffsetPtr = vtableBuffer[i].methodImp.getMeasureRelativeOffset()
//當然,這里的減去虛擬內存基地址邏輯,還可以添加一個func在相對指針內部偏移時實現
let imp_Decimal = numericCast(Int(bitPattern: impOffsetPtr)) - vmAddressSize
let impPtr = UnsafeRawPointer(bitPattern: UInt(imp_Decimal))
let function = unsafeBitCast(impPtr, to: Function.self)
function()
}
}
打印信息
Class Name: LGTeacher
Class type: Optional(swiftTest.LGTeacher)
age->Int->18
name->String->Kody
teach
teach1
teach2
六.還原TargetStructMetadata
還原結構體名稱
、屬性名稱
、屬性類型
、屬性值
查看源碼路線和TargetEnumMetadata
一致,這里由于篇幅問題就不一一還原了。大概邏輯可以看看TargetEnumMetadata
可以看出基本上與值類型的
TargetEnumMetadata
共用一套邏輯Struct
是結構體,它的內存地址就是指向的第一個元素。因此它的第一個屬性偏移量肯定就為0
struct TargetStructMetadata {
var kind: Int
var descriptor: UnsafeMutablePointer<TargetStructDescriptor>
}
struct TargetStructDescriptor {
var flags: UInt32
var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
var name: TargetRelativeDirectPointer<CChar>
var accessFunctionPtr: TargetRelativeDirectPointer<UnsafeRawPointer>
var fields: TargetRelativeDirectPointer<FieldDescriptor>
var numFields: UInt32
var fieldOffsetVectorOffset: UInt32
}
struct FieldDescriptor {
var mangledTypeName: TargetRelativeDirectPointer<UInt8>
var superClass: TargetRelativeDirectPointer<CChar>
var kind: UInt16
var fieldRecordSize: UInt16
var numField: UInt32
//連續的內存空間存放我們的var變量/case的名稱
//UnsafeBufferPointer,連續的內存空間指針
var fieldRecords: FieldRecordBuffer<FieldRecord>
/*
kind: FieldDescriptorKind(枚舉值依次遞增)
Struct: 0x1
Class
Enum
MultiPayloadEnum
Protocol
ClassProtocol
ObjCProtocol
ObjCClass
*/
}
struct FieldRecordBuffer<Element> {
var element: Element
//方式1.返回buffer連續內存空間
mutating func buffer(count: Int) -> UnsafeMutableBufferPointer<Element> {
return withUnsafePointer(to: &self) {
return UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(start: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self), count: count))
}
}
//方式2.根據下標獲取數組元素
mutating func getFieldRecord(index: Int) -> UnsafeMutablePointer<Element> {
return withUnsafePointer(to: &self) {
return UnsafeMutablePointer(mutating: UnsafeRawPointer($0.advanced(by: index)).assumingMemoryBound(to: Element.self))
}
}
}
struct FieldRecord {
var flags: UInt32
var mangledTypeName: TargetRelativeDirectPointer<UInt8>
var fieldName: TargetRelativeDirectPointer<CChar>
/*
flags: FieldRecordFlags
IsIndirectCase = 0x1,
IsVar = 0x2,
IsArtificial = 0x4,
其它情況 = 0
*/
}
struct TargetRelativeDirectPointer<Pointee> {
var offset: Int32
mutating func getMeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
let offset = self.offset
return withUnsafePointer(to: &self) {
/*
注意:這里的$0必須轉化為UnsafeRawPointer再執行advanced
如果類型指針advanced會出問題。
原始是:在介紹指針的時候說過,如果是類型指針的話,
advanced(x)相當于移動該類型的內存大小*x。
*/
return UnsafeMutableRawPointer(mutating: UnsafeRawPointer($0).advanced(by: numericCast(offset))).assumingMemoryBound(to: Pointee.self)
}
}
}
@_silgen_name("swift_getTypeByMangledNameInContext")
private func getTypeByMangledNameInContext(
mangledNamePtr: UnsafePointer<UInt8>,
mangledNameLength: Int,
genericContext: UnsafeRawPointer?,
genericArgs: UnsafeRawPointer?) -> Any.Type?
func getTypeByMangleName(
mangledNamePtr: UnsafePointer<UInt8>,
genericContext: UnsafeRawPointer?, metadata: UnsafeRawPointer) -> Any.Type? {
return getTypeByMangledNameInContext(mangledNamePtr: mangledNamePtr,
mangledNameLength: getMangledTypeNameSize(mangledNamePtr),
genericContext: UnsafeRawPointer(genericContext),
genericArgs: getGenericArgs(metadata))
}
//參考HandyJson,暫時沒找到測量mangledName的長度
func getMangledTypeNameSize(_ managedName: UnsafePointer<UInt8>) -> Int {
// TODO: should find the actually size
return 256
}
//獲取泛型參數在metadata中的偏移量
func getGenericArgumentOffset() -> Int {
return 2
}
//如果有泛型參數,需要傳入的泛型參數指針。參考HandyJson
func getGenericArgs(_ metadata: UnsafeRawPointer) -> UnsafeRawPointer? {
let offSetPtr = metadata.advanced(by: getGenericArgumentOffset() * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Int.self)
if offSetPtr.pointee == 0 {
return nil
}
return UnsafeRawPointer(offSetPtr)
}
main.swift
struct LGTeacher {
var age = 18
var name = "Kody"
}
protocol AnyExtensions {}
extension AnyExtensions {
static func value(ptr: UnsafeRawPointer) -> Any {
print(self)
return ptr.assumingMemoryBound(to: self).pointee
}
}
struct TargetProtocolMetadata {
let type: Any.Type
let witness: Int
}
var ptr = unsafeBitCast(LGTeacher.self as Any.Type, to: UnsafeMutablePointer<TargetStructMetadata>.self)
print("Struct Kind: 0x\(String(ptr.pointee.kind, radix: 16))") //0x200 => Struct
let descriptorPtr = ptr.pointee.descriptor
print("Struct Name: \(String(cString: descriptorPtr.pointee.name.getMeasureRelativeOffset()))")
let fieldDescriptor = ptr.pointee.descriptor.pointee.fields.getMeasureRelativeOffset()
print("Struct Type: \(getTypeByMangleName(mangledNamePtr: fieldDescriptor.pointee.mangledTypeName.getMeasureRelativeOffset(), genericContext: descriptorPtr, metadata: ptr) as Any)")
/*
獲取屬性偏移信息
Struct和Class不一致,Class每8字節存放偏移信息。Struct每4字節存放偏移信息
*/
let offSets = UnsafeRawPointer(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self).advanced(by: numericCast(descriptorPtr.pointee.fieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self)
var t = LGTeacher()
let instanceAddress = withUnsafePointer(to: &t) {$0}
for i in 0..<descriptorPtr.pointee.numFields {
let fieldRecordPtr = fieldDescriptor.pointee.fieldRecords.getFieldRecord(index: numericCast(i))
let fieldType = getTypeByMangleName(mangledNamePtr: fieldRecordPtr.pointee.mangledTypeName.getMeasureRelativeOffset(), genericContext: descriptorPtr, metadata: ptr)!
let fieldName = String(cString: fieldRecordPtr.pointee.fieldName.getMeasureRelativeOffset())
//方式1:
// struct Extensions: AnyExtensions {}
// var extensions: AnyExtensions.Type = Extensions.self
// withUnsafePointer(to: &extensions) {
// UnsafeMutableRawPointer(mutating: $0).assumingMemoryBound(to: Any.Type.self).pointee = fieldType
// }
// let value = extensions.value(ptr: UnsafeRawPointer(instanceAddress).advanced(by: Int(offSets[numericCast(i)])))
//
//方式2:
let structMetadata = TargetProtocolMetadata(type: fieldType, witness: 0)
let protocolType = unsafeBitCast(structMetadata, to: AnyExtensions.Type.self)
let value = protocolType.value(ptr: UnsafeRawPointer(instanceAddress).advanced(by: Int(offSets[numericCast(i)])))
print("\(fieldName)->\(fieldType)->\(value)")
}
打印信息
Struct Kind: 0x200
Struct Name: LGTeacher
Struct Type: Optional(swiftTest.LGTeacher)
Int
age->Int->18
String
name->String->Kody
問題1:關于fieldOffsetVectorOffset
的值
fieldOffsetVectorOffset
表示基于matadata
的偏移量。如果沒有其它數據的影響的話(比如說繼承,繼承后fieldOffsetVectorOffset+1),就存放在metadata的后面。
TargetClassMetadata
大小就是10x8字節,因此對應的fieldOffsetVectorOffset
為10。
TargetStructMetadata
大小就是2x8字節,因此對應的fieldOffsetVectorOffset
為2。
對于偏移值來說
對于Class來說,第一個偏移信息肯定為16。除去HeapObject
內存大小
對于Struct來說,第一個偏移信息肯定為0。值類型數據,空間地址就是第一條數據內存空間
問題2:關于變量偏移信息,Class每8字節存放偏移信息。Struct每4字節存放偏移信息
暫未在源碼中找到相應的邏輯
七.關于HandyJson源碼理解
待完善