Swift進(jìn)階教程Mirror反射示例詳解
元類型與.self
AnyObject
在Swift開(kāi)發(fā)中,我們經(jīng)常會(huì)使用AnyObject來(lái)代表任意類的實(shí)例、類的類型、以及僅類遵守的協(xié)議。
代表任意類的實(shí)例、類的類型
class LGTeacher { var age = 18 } var t = LGTeacher() var t1: AnyObject = t //代表LGTeacher類的實(shí)例 var t2: AnyObject = LGTeacher.self //代表LGTeacher類的類型
代表僅類遵循的協(xié)議
這里使用了AnyObjcct來(lái)修飾協(xié)議,可以看到,當(dāng)struct類型的structLGTeacher去遵循協(xié)議時(shí),編譯器會(huì)報(bào)錯(cuò)。只允許class類型遵循協(xié)議。
我們?cè)诤蚈C交互的過(guò)程中,也經(jīng)常通過(guò)AnyObject來(lái)表示某種類型的instance。
我們?cè)诖a編寫(xiě)的過(guò)程中有時(shí)候不知道具體的類型,?AnyObject來(lái)表示;那如果我們知道了確定了類型,該如何把AnyObject轉(zhuǎn)換成具體的類型,這?我們使?三個(gè)關(guān)鍵字as,as?,as!進(jìn)行類型轉(zhuǎn)換。
AnyClass
AnyClass代表了任意實(shí)例的類型,我們可以從源碼里面去查看AnyClass的定義
public typealias AnyClass = AnyObject.Type
我們可以看到,AnyClass的定義就是AnyObject.Type,也就是實(shí)例對(duì)象的類型,所以我們不能用具體的實(shí)例對(duì)象賦值給AnyClass,編譯器會(huì)報(bào)錯(cuò)。
Any
Any可以代表任意類型(枚舉、結(jié)構(gòu)體、類),也包括函數(shù)類型和Optional類型。
var array:[AnyObject] = [1,2]
上面這段代碼會(huì)報(bào)錯(cuò),因?yàn)锳nyObject代表任意類的實(shí)例和類型,而我們傳入的是Int類型,是屬于值類型,無(wú)法用AnyObject表示。這時(shí),我們要使用Any。
var array:[Any] = [1,2]
type(Of:)
type(Of:)?來(lái)獲取?個(gè)值的動(dòng)態(tài)類型。什么是動(dòng)態(tài)類型呢?
- 靜態(tài)類型(static type),這個(gè)是在編譯時(shí)期確定的類型。
- 動(dòng)態(tài)類型(dynamic Type),這個(gè)是在運(yùn)?時(shí)期確定的類型。
接下來(lái)我們用代碼來(lái)描述靜態(tài)類型和動(dòng)態(tài)類型。
可以看到,在編譯期間就能知道a的類型是Int,因?yàn)槌跏蓟瘮?shù)據(jù)的時(shí)候賦值的是Int類型數(shù)據(jù),但是在test方法的形參定義的類型是Any,因此在編譯期間并不知道其形參類型,所以將a傳入時(shí)也被編譯器當(dāng)作是Any類型,但是在運(yùn)行時(shí),可以動(dòng)態(tài)得知傳入的a是Int類型,因此type(of:)就可以用來(lái)獲取當(dāng)前值在運(yùn)行時(shí)的實(shí)際類型。
self
在Swift中,我們可以使用類型或者實(shí)例對(duì)象來(lái)訪問(wèn)self。
T.self: T 是實(shí)例對(duì)象,當(dāng)前 T.self 返回的就是實(shí)例對(duì)象本身。
如果 T 是類,當(dāng)前 T.self 返回的就是元類型。
我們通過(guò)代碼驗(yàn)證一下。
從代碼中,我們可以看到,當(dāng)我po t 和po t1時(shí),可以看到t和t1指向同一個(gè)地址。然后當(dāng)我po t2時(shí),發(fā)現(xiàn)打印出來(lái)的是LGTeaher類型。接下來(lái)我用x/8g命令打印t2,打印出t2的內(nèi)存地址。再打印出t的內(nèi)存地址,然后打印t里面存儲(chǔ)metadata的內(nèi)存地址。發(fā)現(xiàn)和t2的內(nèi)存地址一模一樣。
所以,在 T.self 中,當(dāng) T 為實(shí)例對(duì)象的時(shí)候,T.self 返回的是實(shí)例對(duì)象本身。當(dāng) T 為類的時(shí)候,T.self 返回的是一個(gè)元類型,也就是前面講的元數(shù)據(jù)(metadata)。
self在方法里面的作用
我們可以看到,在實(shí)例方法中,self指向的就是當(dāng)前調(diào)用方法的實(shí)例對(duì)象,而在類方法里面,self指向的就是當(dāng)前類型的元數(shù)據(jù)。
Self引用
Self類型不是特定類型,?是讓您?便地引?當(dāng)前類型,??需重復(fù)或知道該類型的名稱。
在協(xié)議聲明或協(xié)議成員聲明中,Self類型是指最終符合協(xié)議的類型。
Self作為實(shí)例方法的返回類型代表自身類型
計(jì)算屬性/實(shí)例方法中訪問(wèn)自身的類型屬性,類型方法
Swift Runtime
在OC中,我們可以通過(guò)Runtime特性來(lái)獲取一個(gè)類的屬性和方法。而Swift中沒(méi)有Runtime,能使用OC的Runtime獲取一個(gè)類的屬性和方法嗎?我們通過(guò)代碼測(cè)試一下。
代碼結(jié)果沒(méi)有打印任何東西。
現(xiàn)在我們往LGTeacher類的屬性和方法前面加上@objc標(biāo)識(shí)符,看看會(huì)有什么結(jié)果?
這時(shí)可以通過(guò)Runtime API打印方法和屬性名,但是OC無(wú)法進(jìn)行調(diào)動(dòng)。
對(duì)于繼承NSObject類的Swift類,如果我們想要?jiǎng)討B(tài)的獲取當(dāng)前的屬性和?法,必須在其聲明前添加@objc 關(guān)鍵字,否則也是沒(méi)有辦法通過(guò)Runtime API獲取的。
繼承NSObject類的Swift類,沒(méi)有在屬性和方法聲明前面添加@objc 關(guān)鍵字,只能獲取到init方法。
繼承NSObject類的Swift類,在屬性和方法聲明前面添加@objc關(guān)鍵字,不僅可以使用Runtime API獲取屬性和方法名,也可以在OC中被調(diào)用。
還有一些和Swift Runtime相關(guān)的結(jié)論,我在這里總結(jié)出來(lái),可以自己去試驗(yàn)一下。
- 純swift類沒(méi)有動(dòng)態(tài)性,但在?法、屬性前添加dynamic修飾,可獲得動(dòng)態(tài)性。
- 繼承?NSObject的swift類,其繼承??類的?法具有動(dòng)態(tài)性,其它?定義?法、屬性想要獲得動(dòng)態(tài)性,需要添加dynamic修飾。
- 若?法的參數(shù)、屬性類型為swift特有、?法映射到objective-c的類型(如Character、Tuple),則 此?法、屬性?法添加dynamic修飾(編譯器報(bào)錯(cuò))
Mirror
Mirror的基本用法
所謂反射就是可以動(dòng)態(tài)獲取類型、成員信息,在運(yùn)?時(shí)可以調(diào)??法、屬性等?為的特性。在使?OC開(kāi)發(fā)時(shí)很少?gòu)?qiáng)調(diào)其反射概念,因?yàn)镺C的Runtime要?其他語(yǔ)?中的反射強(qiáng)?的多。但是 Swift 是??類型安全的語(yǔ)?,不?持我們像 OC 那樣直接操作,它的標(biāo)準(zhǔn)庫(kù)仍然提供了反射機(jī)制來(lái)讓我們?cè)L問(wèn)成員信息,Swift 的反射機(jī)制是基于?個(gè)叫 Mirror 的結(jié)構(gòu)體來(lái)實(shí)現(xiàn)的。然后就可以通過(guò)它查詢這個(gè)實(shí)例。
Mirror的基本使用如下
class LGTeacher { var age:Int = 18 func teach() { print("teach") } } //?先通過(guò)構(gòu)造?法構(gòu)建?個(gè)Mirror實(shí)例,這?傳?的參數(shù)是 Any,也就意味著當(dāng)前可以是類,結(jié)構(gòu)體,枚舉等 let mirror = Mirror(reflecting: LGTeacher()) //接下來(lái)遍歷 children 屬性,這是?個(gè)集合 for pro in mirror.children { //然后我們可以直接通過(guò) label 輸出當(dāng)前的名稱,value 輸出當(dāng)前反射的值 print("\(pro.label) : \(pro.value)") }
Mirror的簡(jiǎn)單應(yīng)用-JSON解析
class LGTeacher { var age:Int = 18 var name = "FY" } enum JSONMapError: Error { case emptyKey case notConformProtocol } protocol JSONMap { func jsonMap() -> Any } extension JSONMap { func jsonMap() -> Any { let mirror = Mirror(reflecting: self) guard !mirror.children.isEmpty else { return self } var result: [String: Any] = [:] for child in mirror.children { if let value = child.value as? JSONMap { if let key = child.label { result[key] = try? value.jsonMap() } } else { return JSONMapError.notConformProtocol } } return result } } extension LGTeacher: JSONMap{} extension Int: JSONMap{} extension String: JSONMap{} print(LGTeacher().jsonMap()) // ["age": 18, "name": "FY"]
Mirror源碼解析
?先我們現(xiàn)在源?件??搜索Mirror.Swift,在源碼中我們可以很清晰的看到Mirror是由結(jié)構(gòu)體實(shí)現(xiàn)的,我們忽略掉?些細(xì)節(jié),快速定位到初始化的?法
public init(reflecting subject: Any) { if case let customized as CustomReflectable = subject { self = customized.customMirror } else { self = Mirror(internalReflecting: subject) } }
可以看到,這?接受?個(gè)Any類型的參數(shù),同樣的這?有?個(gè)if case的寫(xiě)法來(lái)判斷當(dāng)前的subject是否遵循了customReflectable協(xié)議,如果是我們就直接調(diào)?customMirror, 否則就進(jìn)?下級(jí)函數(shù)的調(diào)?。
這?有兩個(gè)需要注意的點(diǎn)if case的寫(xiě)法,這?其實(shí)枚舉Case的模式匹配,和我們的Switch?樣,這?是只有?個(gè)case的 switch 語(yǔ)句。
于此同時(shí)這?出現(xiàn)了?個(gè)customRefletable的協(xié)議。我們來(lái)看一下它的用法。?先我們遵循 customReflectable 協(xié)議,并實(shí)現(xiàn)其中的屬性customMirror,customMirror會(huì)返回?個(gè)Mirror對(duì)象。代碼如下:
這里通過(guò)遵循customReflectable 協(xié)議并實(shí)現(xiàn)了其中的計(jì)算屬性customMirror,主要作用是當(dāng)我們使用lldb debug的時(shí)候,可以提供詳細(xì)的屬性信息。
我們接下來(lái)看如果不遵循customRefletable協(xié)議的類,那么就會(huì)走M(jìn)irror(internalReflecting: subject)代碼。
全局搜索internalReflecting,在ReflectionMirror.swift文件里面找到了這個(gè)方法的具體實(shí)現(xiàn)。
從代碼里面我們可以看到,首先需要獲取subject的真實(shí)類型信息。然后再獲取subject的屬性信息。 而獲取subject的真實(shí)類型信息則是通過(guò)_getNormalizedType這個(gè)方法來(lái)獲取的。搜索這個(gè)方法,然后我們就可以找到它的代碼。
這里使用了一個(gè)編譯器字段 @ silgen_name 其實(shí)是 Swift 的一個(gè)隱藏符號(hào),作用是將某個(gè) C/C++語(yǔ)言函數(shù)直接映射為 Swift 函數(shù)。也可以理解為為 C++ 代碼的 swift_reflectionMirror_normalizedType 函數(shù)定義一個(gè)在 swift 中使用的別名 _getNormalizedType。
所以調(diào)用了_getNormalizedType方法實(shí)際上是調(diào)用了swift_reflectionMirror_normalizedType方法,我在ReflectionMirror.cpp文件中找到了具體實(shí)現(xiàn)。
從代碼里面可以知道,通過(guò)call函數(shù)調(diào)用了ReflectionMirrorImpl類,然后返回這個(gè)類的類型。
我們先看一下ReflectionMirrorImpl類的具體內(nèi)容。
從注釋中,我們可以知道,這是一個(gè)抽象基類,也就是說(shuō)不同的類型反射需要不同的類實(shí)現(xiàn)。
我們接下來(lái)看一下call函數(shù)的具體實(shí)現(xiàn)
在call函數(shù)中有一個(gè)Switch方法。根據(jù)不同的類型,調(diào)用不同的ReflectionMirrorImpl類。 我們就取EnumIpml類去探個(gè)究竟。
上面代碼就是EnumIpml類的具體實(shí)現(xiàn)。首先是isReflectable()這個(gè)方法。這個(gè)方法返回這個(gè)類型是否可以被反射,也就是找到metadata,再找到metadata中存儲(chǔ)的Description,通過(guò)它里面存儲(chǔ)的 isReflectable來(lái)確定。
接下來(lái)我們看一下getInfo方法。在代碼里面我們可以看到,獲取name、info屬性信息主要是通過(guò)getFieldAt來(lái)獲取。我們現(xiàn)在就去查看getFieldAt方法。具體代碼如下:
static std::pair<StringRef /*name*/, FieldType /*fieldInfo*/> getFieldAt(const Metadata *base, unsigned index) { using namespace reflection; 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 result = swift_getTypeByMangledName( MetadataState::Complete, typeName, substitutions.getGenericArgs(), [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }); // If demangling the type failed, pretend it's an empty type instead with // a log message. TypeInfo typeInfo; if (result.isError()) { typeInfo = TypeInfo({&METADATA_SYM(EMPTY_TUPLE_MANGLING), MetadataState::Complete}, {}); auto *error = result.getError(); char *str = error->copyErrorString(); missing_reflection_metadata_warning( "warning: the Swift runtime was unable to demangle the type " "of field '%*s'. the mangled type name is '%*s': %s. this field will " "show up as an empty tuple in Mirrors\n", (int)name.size(), name.data(), (int)typeName.size(), typeName.data(), str); error->freeErrorString(str); } else { typeInfo = result.getType(); } auto fieldType = FieldType(typeInfo.getMetadata()); fieldType.setIndirect(field.isIndirectCase()); fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership()); fieldType.setIsVar(field.isVar()); return {name, fieldType};
這里可以看到可以看到 所有的信息都是通過(guò)Metadata、getDescription()、FieldDescrition 這幾個(gè)東西來(lái)去實(shí)現(xiàn)的,?個(gè)是當(dāng)前類型的元數(shù)據(jù),?個(gè)是當(dāng)前類型的描述,?個(gè)是對(duì)當(dāng)前類型屬性的描述。
Enum Metadata探索
我們?cè)?a href="http://www.dbjr.com.cn/article/74373.htm" rel="external nofollow" target="_blank">類和結(jié)構(gòu)體這篇文章里面,描述了類的Metadata結(jié)構(gòu),并把它的C++代碼轉(zhuǎn)換成了Swift代碼。我們這次來(lái)嘗試轉(zhuǎn)換Enum和struct的MetaData結(jié)構(gòu)。首先來(lái)探索Enum的Metadata結(jié)構(gòu)。
還原TargetEnumMetadata
通過(guò)源碼全局搜索EnumMetadata,我們找到了TargetEnumMetadata。沿著TargetEnumMetadata的繼承鏈往上查找,TargetEnumMetadata-> TargetValueMetadata -> TargetMetadata
struct TargetEnumMetadata : public TargetValueMetadata<Runtime> { } struct TargetValueMetadata : public TargetMetadata<Runtime> { TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> *Description; } struct TargetMetadata { StoredPointer Kind; }
從上面的源碼中我們可以知道,TargetMetadata有一個(gè)屬性Kind,這個(gè)Kind主要是存儲(chǔ)MetadataKind類,是個(gè)int_32類型。TargetValueMetadata里面有Description屬性,因此我們可以把TargetEnumMetadata轉(zhuǎn)成這樣的結(jié)構(gòu)體
struct TargetEnumMetadata { var kind: Int var typeDescriptor: UnsafeRawPointer }
還原TargetEnumDescriptor
接下來(lái),我們要還原typeDescriptor的結(jié)構(gòu),雖然在TargetValueMetadata類中是TargetValueTypeDescriptor類,而我們?cè)赥argetMetadata發(fā)現(xiàn)Description屬性是TargetEnumDescriptor類,所以,Description屬性和TargetMetadata一樣,應(yīng)該也是有繼承鏈的。
在TargetMetadata源碼中,獲取Description屬性的方法是這樣的:
const TargetEnumDescriptor<Runtime> *getDescription() const { return llvm::cast<TargetEnumDescriptor<Runtime>>(this->Description); }
然后我們?nèi)ピ创a里面查看TargetEnumDescriptor類,得到它的繼承鏈TargetEnumDescriptor-> TargetValueTypeDescriptor -> TargetTypeContextDescriptor-> TargetContextDescriptor 它們包含的屬性的代碼如下:
class TargetEnumDescriptor final : public TargetValueTypeDescriptor<Runtime>, public TrailingGenericContextObjects<TargetEnumDescriptor<Runtime>, TargetTypeGenericContextDescriptorHeader, /*additional trailing objects*/ TargetForeignMetadataInitialization<Runtime>, TargetSingletonMetadataInitialization<Runtime>, TargetCanonicalSpecializedMetadatasListCount<Runtime>, TargetCanonicalSpecializedMetadatasListEntry<Runtime>, TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>> { uint32_t NumPayloadCasesAndPayloadSizeOffset; uint32_t NumEmptyCases; } class TargetValueTypeDescriptor: public TargetTypeContextDescriptor<Runtime> { } class TargetTypeContextDescriptor: public TargetContextDescriptor<Runtime> { TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name; TargetRelativeDirectPointer<Runtime, MetadataResponse(...), /*Nullable*/ true> AccessFunctionPtr; TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,/*nullable*/ true> Fields; } struct TargetContextDescriptor { ContextDescriptorFlags Flags; TargetRelativeContextPointer<Runtime> Parent; }
從上面的代碼我們可以把TargetEnumDescriptor使用Swift把它還原出來(lái),還原的代碼如下:
struct TargetEnumDescriptor{ var flags: Int32 var parent: TargetRelativeDirectPointer<UnsafeRawPointer> var name: TargetRelativeDirectPointer<CChar> var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer> var fieldDescriptor: TargetRelativeDirectPointer<UnsafeRawPointer> var NumPayloadCasesAndPayloadSizeOffset: UInt32 var NumEmptyCases: UInt32 }
此時(shí)TargetMetadata中的數(shù)據(jù)結(jié)構(gòu)也可以修改一下
struct TargetEnumMetadata{ var kind: Int var typeDescriptor: UnsafeMutablePointer<TargetEnumDescriptor> }
相對(duì)偏移指針
在上面的源碼中,我們可以發(fā)現(xiàn),TargetValueTypeDescriptor中的屬性、比如name、Fields等,他們的類型都是用TargetRelativeDirectPointer來(lái)定義。我們現(xiàn)在來(lái)看一下TargetRelativeDirectPointer是什么?
從上面的源碼定義可以知道,它是一個(gè)模板類,(接收三個(gè)參數(shù),?個(gè)是Runtime, ?個(gè)是Pointee , Bool類型默認(rèn)為T(mén)rue)。接下來(lái)我們看一下RelativeDirectPointer。
這個(gè)指針類代碼比較簡(jiǎn)單,其中 T 就是我們進(jìn)來(lái)的類型,Offset就是int32_t 的類型,從字面意思上看應(yīng)該是偏移量之類的。我們?cè)倏聪滤膅et()方法。
從以上代碼,我們可以看出TargetRelativeDirectPointer應(yīng)該是一個(gè)用來(lái)相對(duì)尋址的指針類。在 Swift中引??個(gè)實(shí)例對(duì)象有兩種情況:一種是直接尋址,另外一種是相對(duì)尋址。比如 TargetEnumDescriptor 中的 Name,這個(gè) Name 存儲(chǔ)的值并不是 Name 表意上的值,Name存儲(chǔ)的是一個(gè)叫做相對(duì)偏移量或者叫偏移信息。此時(shí),我們拿到 Name 的值的內(nèi)存地址做法是:Name 的內(nèi)存地址 + 相對(duì)偏移量。在 Swift 里面有很多這樣的偏移信息,這樣做可以節(jié)省內(nèi)存空間,避免存儲(chǔ)大量的內(nèi)存地址。
對(duì)此,我們把TargetRelativeDirectPointer給還原出來(lái)。還原代碼如下:
struct TargetRelativeDirectPointer<Pointee>{ var offset: Int32 mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee>{ let offset = **self**.offset return withUnsafePointer(to: &self) { p in return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self)) } } }
打印枚舉中的屬性
最后,我們來(lái)打印一下枚舉中的屬性。代碼如下:
enum Planet { case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune } let ptr = unsafeBitCast(Planet.self as Any.Type to:UnsafeMutablePointer<TargetEnumMetadata>.self) let namePtr = ptr.pointee.typeDescriptor.pointee.name.getmeasureRelativeOffset() print("name: ",String(cString: namePtr)) print("NumPayloadCasesAndPayloadSizeOffset ",ptr.pointee.typeDescriptor.pointee.NumPayloadCasesAndPayloadSizeOffset) print("NumEmptyCases ",ptr.pointee.typeDescriptor.pointee.NumEmptyCases)
打印結(jié)果如下
name: Planet NumPayloadCasesAndPayloadSizeOffset 0 NumEmptyCases 8
Struct Metadata探索
現(xiàn)在我們來(lái)解析一下struct類型的Metadata。
首先,和上面的Enum一樣,通過(guò)全局搜索,找到struct的MetaData是TargetStructMetadata類。通過(guò)它的繼承鏈TargetStructMetadata-> TargetValueMetadata -> TargetMetadata可以知道,TargetStructMetadata的數(shù)據(jù)結(jié)構(gòu)和TargetEnumMetadata一樣,因此,我們可以還原一下 TargetStructMetadata的數(shù)據(jù)結(jié)構(gòu)如下:
struct TargetStructMetadata { var kind: Int var typeDescriptor: UnsafeRawPointer }
然后,我們?nèi)ふ襱ypeDescriptor的類型,從TargetStructMetadata的源碼中,我們找到typeDescriptor的類型是TargetStructDescriptor。我們?nèi)ニ阉鱐argetStructDescriptor的源碼,得到了TargetStructDescriptor類的定義以及屬性如下:
class TargetStructDescriptor final: public TargetValueTypeDescriptor<Runtime>, public TrailingGenericContextObjects<TargetStructDescriptor<Runtime>, TargetTypeGenericContextDescriptorHeader, /*additional trailing objects*/ TargetForeignMetadataInitialization<Runtime>, TargetSingletonMetadataInitialization<Runtime>, TargetCanonicalSpecializedMetadatasListCount<Runtime>, TargetCanonicalSpecializedMetadatasListEntry<Runtime>, TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>> { uint32_t NumFields; uint32_t FieldOffsetVectorOffset; }
從源碼中我們可以看到,TargetStructDescriptor繼承自TargetValueTypeDescriptor,因此繼承鏈和TargetEnumDescriptor一樣,我們還原出來(lái)的TargetStructDescriptor的數(shù)據(jù)結(jié)構(gòu)如下:
struct TargetStructDescriptor{ var flags: Int32 var parent: TargetRelativeDirectPointer<UnsafeRawPointer> var name: TargetRelativeDirectPointer<CChar> var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer> var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor> var NumFields: UInt32 var FieldOffsetVectorOffset: UInt32 ``` func getFieldOffsets(_ metadata: UnsafeRawPointer) -> UnsafePointer<Int32> { return UnsafeRawPointer(metadata.assumingMemoryBound(to: Int.self).advanced(by: numericCast(self.FieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self) } //參考handyjson 中 var genericArgumentOffset: Int { return 2 }
}
此時(shí)`TargetStructMetadata`中的數(shù)據(jù)結(jié)構(gòu)也可以修改一下 ```swift struct TargetStructMetadata{ var kind: Int var typeDescriptor: UnsafeMutablePointer<TargetStructDescriptor> }
接著我們還原FieldDescriptor的數(shù)據(jù)結(jié)構(gòu)。我們先找到FieldDescriptor類的源碼,找出它的屬性,和方法,代碼如下:
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; llvm::ArrayRef<FieldRecord> getFields() const { return {getFieldRecordBuffer(), NumFields}; } }
我們來(lái)看一下getFields()方法,這個(gè)方法就是獲取 fields 的方法,fields 存的是 FieldRecords,通過(guò)getFieldRecordBuffer()來(lái)讀取FieldRecords。
在getFieldRecordBuffer()方法中,通過(guò) reinterpret_cast 將 (this + 1) 強(qiáng)制轉(zhuǎn)換成 FieldRecord * 類型。所以我們可以推測(cè)這個(gè) fields 是一塊連續(xù)的內(nèi)存空間,這一塊連續(xù)的內(nèi)存空間存儲(chǔ)的是 FieldRecord 類型,并且 NumFields 是它的容量大小。
在 C++ 中, this 是一個(gè)指向該對(duì)象的指針,由于它是一個(gè)指針,因此它可以應(yīng)用指針?biāo)阈g(shù)甚至數(shù)組索引。如果這個(gè) this 是數(shù)組中的一個(gè)元素,(this + 1) 則將指向數(shù)組中的下一個(gè)對(duì)象。
所以我們可以把FieldDescriptor的數(shù)據(jù)結(jié)構(gòu)還原成下面結(jié)構(gòu)。
struct FieldDescriptor { var MangledTypeName: TargetRelativeDirectPointer<CChar> var Superclass: TargetRelativeDirectPointer<CChar> var Kind: UInt16 var FieldRecordSize:UInt16 var NumFields: UInt32 var fields: FieldRecordBuffer<FieldRecord> }
其中FieldRecordBuffer我們可以把它還原成一個(gè)有連續(xù)空間的數(shù)組,容量為NumFields,存儲(chǔ)的是FieldRecord。還原結(jié)構(gòu)如下:
struct FiledRecordBuffer<Element>{ var element: Element mutating func buffer(n: Int) -> UnsafeBufferPointer<Element> { return withUnsafePointer(to: &self) { let ptr = $0.withMemoryRebound(to: Element.self, capacity: 1) { start in return start } return UnsafeBufferPointer(start: ptr, count: n) } } mutating func index(of i: Int) -> UnsafeMutablePointer<Element> { return withUnsafePointer(to: &self) { return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self).advanced(by: i)) } } }
最后我們看一下FieldRecord的源碼,得到的源碼屬性如下:
class FieldRecord { const FieldRecordFlags Flags; public: const RelativeDirectPointer<const char> MangledTypeName; const RelativeDirectPointer<const char> FieldName; }
還原FieldRecord得到的數(shù)據(jù)結(jié)構(gòu)如下:
struct FieldRecord { var Flags: UInt32 var MangledTypeName: TargetRelativeDirectPointer<CChar> var FieldName: TargetRelativeDirectPointer<CChar> }
至此,我們把結(jié)構(gòu)體的MetaData結(jié)構(gòu)都還原出來(lái)。
獲取結(jié)構(gòu)體的屬性
現(xiàn)在我們來(lái)驗(yàn)證一下結(jié)構(gòu)體,獲取結(jié)構(gòu)體的屬性。代碼如下:
@_silgen_name("swift_getTypeByMangledNameInContext") func swift_H_getTypeByMangledNameInContext(typeName: UnsafeRawPointer, len: Int, context: UnsafeRawPointer, generic: UnsafeRawPointer) -> UnsafeRawPointer protocol BridgeProtocol { } extension BridgeProtocol { static func get(from pointer: UnsafeRawPointer) -> Any { pointer.assumingMemoryBound(to: Self.self).pointee } } struct BridgeProtocolMetadata { let type: Any.Type let witness: Int } func customCast(type: Any.Type) -> BridgeProtocol.Type { let container = BridgeProtocolMetadata(type: type, witness: 0) let cast = unsafeBitCast(container, to: BridgeProtocol.Type.self) return cast } struct LGStudent { var age = 18 var name = "FWJ" let money = 2000 } var t = LGStudent() let ptr = unsafeBitCast(LGStudent.self as Any.Type, to: UnsafeMutablePointer<TargetStructMetadata>.self) print("----------開(kāi)始解析---------------") let namePtr = ptr.pointee.typeDescriptor.pointee.name.getmeasureRelativeOffset() let filedNum = ptr.pointee.typeDescriptor.pointee.NumFields print("當(dāng)前結(jié)構(gòu)體的名稱: \(String(cString: namePtr))") print("當(dāng)前結(jié)構(gòu)體的屬性數(shù)量 \(filedNum)") print("============開(kāi)始解析屬性============") let offsets = ptr.pointee.typeDescriptor.pointee.getFieldOffsets(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self)) for i in 0..<filedNum { let fieldRecord = ptr.pointee.typeDescriptor.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)) let fieldOffset = offsets[Int(i)] let fieldName = fieldRecord.pointee.FieldName.getmeasureRelativeOffset() print("--- \(String(cString: fieldName)) 屬性信息 ---") let mangledTypeName = fieldRecord.pointee.MangledTypeName.getmeasureRelativeOffset() print("mangledTypeName: \(String(cString: mangledTypeName))") let typeNameLength = Int(256) let genericVector = UnsafeRawPointer(ptr).advanced(by: ptr.pointee.typeDescriptor.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self) **let** fieldType = swift_H_getTypeByMangledNameInContext(typeName: mangledTypeName, len: typeNameLength, context: UnsafeRawPointer(ptr.pointee.typeDescriptor), generic: genericVector) let type = unsafeBitCast(fieldType, to: Any.Type.self) print("fieldType: \(type)") let brigeProtocolType = customCast(type: type) let instanceAddress = withUnsafePointer(to: &t) { return UnsafeRawPointer($0) } let fieldValue = brigeProtocolType.get(from: instanceAddress.advanced(by: Int(fieldOffset))) print("fieldValue: \(fieldValue)") print("--- \(String(cString: fieldName)) 屬性信息 ---") } //打印結(jié)果 ----------開(kāi)始解析--------------- 當(dāng)前結(jié)構(gòu)體的名稱: LGStudent** 當(dāng)前結(jié)構(gòu)體的屬性數(shù)量 3 ============開(kāi)始解析屬性============ --- age 屬性信息 --- mangledTypeName: Si fieldType: Int fieldValue: 18 --- age 屬性信息 --- --- name 屬性信息 --- mangledTypeName: SS fieldType: String fieldValue: FWJ --- name 屬性信息 --- --- money 屬性信息 --- mangledTypeName: Si fieldType: Int fieldValue: 2000 --- money 屬性信息 ---
swift_getTypeByMangledNameInContext 函數(shù)
這個(gè)函數(shù)的源碼在MetadataLookup.cpp文件中,我們來(lái)看一下它的具體實(shí)現(xiàn)
SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT const Metadata * _Nullable swift_getTypeByMangledNameInContext( const char *typeNameStart, size_t typeNameLength, const TargetContextDescriptor<InProcess> *context, const void * const *genericArgs) { llvm::StringRef typeName(typeNameStart, typeNameLength); SubstGenericParametersFromMetadata substitutions(context, genericArgs); return swift_getTypeByMangledName(MetadataState::Complete, typeName, genericArgs, [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }).getType().getMetadata(); }
這個(gè)函數(shù)返回的是Metadata類型的指針,也就是Swift函數(shù)中的Type類型??梢酝ㄟ^(guò)這個(gè)函數(shù)獲取到每個(gè)函數(shù)的類型。但是這個(gè)函數(shù)是C++函數(shù),需要把它轉(zhuǎn)換成Swift函數(shù)。這里參考了HandyJSON這個(gè)第三方庫(kù),使用@_silgen_name映射成swift函數(shù)。具體代碼如下:
@_silgen_name("swift_getTypeByMangledNameInContext") func swift_H_getTypeByMangledNameInContext(typeName: UnsafeRawPointer, len: Int, context: UnsafeRawPointer, generic: UnsafeRawPointer) -> UnsafeRawPointer
獲取屬性的值
我們可以根據(jù)類型和 FieldOffsetVectorOffset屬性值存儲(chǔ)相對(duì)于實(shí)例的偏移量獲取屬性值,獲取屬性值存儲(chǔ)的指針。參考 HandyJSON 通過(guò)協(xié)議中Self 代表真實(shí)的類型去讀取指針的值。代碼如下:
protocol BridgeProtocol { } extension BridgeProtocol { static func get(from pointer: UnsafeRawPointer) -> Any { pointer.assumingMemoryBound(to: Self.self).pointee } } struct BridgeProtocolMetadata { let type: Any.Type let witness: Int } func customCast(type: Any.Type) -> BridgeProtocol.Type { let container = BridgeProtocolMetadata(type: type, witness: 0) let cast = unsafeBitCast(container, to: BridgeProtocol.Type.self) return cast }
- 這個(gè)函數(shù)傳入一個(gè)Any.Type的類型,通過(guò)它來(lái)創(chuàng)建一個(gè)協(xié)議的 Metadata結(jié)構(gòu)相同的BrigeProtocolMetadata實(shí)例
- 通過(guò) BrigeProtocolMetadata轉(zhuǎn)換成協(xié)議的 BrigeProtocol.Type.self,也就是協(xié)議的 Metadata,那么此時(shí)這個(gè)協(xié)議類型可以獲取到屬性的真實(shí)類型
- 將屬性值指針轉(zhuǎn)換為 Self類型的類型指針,通過(guò)pointee就可以獲取真實(shí)的值
以上就是Swift進(jìn)階教程Mirror反射示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Swift進(jìn)階Mirror反射的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳談swift內(nèi)存管理中的引用計(jì)數(shù)
下面小編就為大家?guī)?lái)一篇詳談swift內(nèi)存管理中的引用計(jì)數(shù)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09Swift3遷移至Swift4可能遇到的問(wèn)題小結(jié)
每當(dāng)看到新的編程語(yǔ)言我總是會(huì)有相當(dāng)大的興趣,所以下面這篇文章主要給大家介紹了關(guān)于Swift3遷移至Swift4可能遇到的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06Swift中的類class與結(jié)構(gòu)體struct體學(xué)習(xí)筆記
和C++一樣,Swfit中同時(shí)擁有類與結(jié)構(gòu)體,能夠充分滿足開(kāi)發(fā)者面向?qū)ο蠛兔嫦蜻^(guò)程編程的需求,這里我們就來(lái)看一下Swift中的類class與結(jié)構(gòu)體struct體學(xué)習(xí)筆記2016-07-07解析Swift語(yǔ)言面相對(duì)象編程中的繼承特性
這篇文章主要介紹了解析Swift語(yǔ)言面相對(duì)象編程中的繼承特性,是Swift入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11Swift編程中的switch...case語(yǔ)句實(shí)例解析
這篇文章主要介紹了Swift編程中的switch...case語(yǔ)句實(shí)例解析,其中重點(diǎn)還是對(duì)于fallthrough關(guān)鍵字用法的講解,需要的朋友可以參考下2016-04-04