欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Swift進(jìn)階教程Mirror反射示例詳解

 更新時間:2022年06月17日 12:39:57   作者:隨風(fēng)飄逝  
這篇文章主要為大家介紹了Swift進(jìn)階教程Mirror反射示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

元類型與.self

AnyObject

在Swift開發(fā)中,我們經(jīng)常會使用AnyObject來代表任意類的實(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來修飾協(xié)議,可以看到,當(dāng)struct類型的structLGTeacher去遵循協(xié)議時,編譯器會報錯。只允許class類型遵循協(xié)議。

我們在和OC交互的過程中,也經(jīng)常通過AnyObject來表示某種類型的instance。

我們在代碼編寫的過程中有時候不知道具體的類型,?AnyObject來表示;那如果我們知道了確定了類型,該如何把AnyObject轉(zhuǎn)換成具體的類型,這?我們使?三個關(guān)鍵字as,as?,as!進(jìn)行類型轉(zhuǎn)換。

AnyClass

AnyClass代表了任意實(shí)例的類型,我們可以從源碼里面去查看AnyClass的定義

public typealias AnyClass = AnyObject.Type

我們可以看到,AnyClass的定義就是AnyObject.Type,也就是實(shí)例對象的類型,所以我們不能用具體的實(shí)例對象賦值給AnyClass,編譯器會報錯。

Any

Any可以代表任意類型(枚舉、結(jié)構(gòu)體、類),也包括函數(shù)類型和Optional類型。

var array:[AnyObject] = [1,2]

上面這段代碼會報錯,因?yàn)锳nyObject代表任意類的實(shí)例和類型,而我們傳入的是Int類型,是屬于值類型,無法用AnyObject表示。這時,我們要使用Any。

var array:[Any] = [1,2]

type(Of:)

type(Of:)?來獲取?個值的動態(tài)類型。什么是動態(tài)類型呢?

  • 靜態(tài)類型(static type),這個是在編譯時期確定的類型。
  • 動態(tài)類型(dynamic Type),這個是在運(yùn)?時期確定的類型。

接下來我們用代碼來描述靜態(tài)類型和動態(tài)類型。

可以看到,在編譯期間就能知道a的類型是Int,因?yàn)槌跏蓟瘮?shù)據(jù)的時候賦值的是Int類型數(shù)據(jù),但是在test方法的形參定義的類型是Any,因此在編譯期間并不知道其形參類型,所以將a傳入時也被編譯器當(dāng)作是Any類型,但是在運(yùn)行時,可以動態(tài)得知傳入的a是Int類型,因此type(of:)就可以用來獲取當(dāng)前值在運(yùn)行時的實(shí)際類型。

self

在Swift中,我們可以使用類型或者實(shí)例對象來訪問self。

T.self: T 是實(shí)例對象,當(dāng)前 T.self 返回的就是實(shí)例對象本身。

如果 T 是類,當(dāng)前 T.self 返回的就是元類型。

我們通過代碼驗(yàn)證一下。

從代碼中,我們可以看到,當(dāng)我po t 和po t1時,可以看到t和t1指向同一個地址。然后當(dāng)我po t2時,發(fā)現(xiàn)打印出來的是LGTeaher類型。接下來我用x/8g命令打印t2,打印出t2的內(nèi)存地址。再打印出t的內(nèi)存地址,然后打印t里面存儲metadata的內(nèi)存地址。發(fā)現(xiàn)和t2的內(nèi)存地址一模一樣。

所以,在 T.self 中,當(dāng) T 為實(shí)例對象的時候,T.self 返回的是實(shí)例對象本身。當(dāng) T 為類的時候,T.self 返回的是一個元類型,也就是前面講的元數(shù)據(jù)(metadata)。

self在方法里面的作用

我們可以看到,在實(shí)例方法中,self指向的就是當(dāng)前調(diào)用方法的實(shí)例對象,而在類方法里面,self指向的就是當(dāng)前類型的元數(shù)據(jù)。

Self引用

Self類型不是特定類型,?是讓您?便地引?當(dāng)前類型,??需重復(fù)或知道該類型的名稱。

在協(xié)議聲明或協(xié)議成員聲明中,Self類型是指最終符合協(xié)議的類型。

Self作為實(shí)例方法的返回類型代表自身類型

計(jì)算屬性/實(shí)例方法中訪問自身的類型屬性,類型方法

Swift Runtime

在OC中,我們可以通過Runtime特性來獲取一個類的屬性和方法。而Swift中沒有Runtime,能使用OC的Runtime獲取一個類的屬性和方法嗎?我們通過代碼測試一下。

代碼結(jié)果沒有打印任何東西。

現(xiàn)在我們往LGTeacher類的屬性和方法前面加上@objc標(biāo)識符,看看會有什么結(jié)果?

這時可以通過Runtime API打印方法和屬性名,但是OC無法進(jìn)行調(diào)動。

對于繼承NSObject類的Swift類,如果我們想要動態(tài)的獲取當(dāng)前的屬性和?法,必須在其聲明前添加@objc 關(guān)鍵字,否則也是沒有辦法通過Runtime API獲取的。

繼承NSObject類的Swift類,沒有在屬性和方法聲明前面添加@objc 關(guān)鍵字,只能獲取到init方法。

繼承NSObject類的Swift類,在屬性和方法聲明前面添加@objc關(guān)鍵字,不僅可以使用Runtime API獲取屬性和方法名,也可以在OC中被調(diào)用。

還有一些和Swift Runtime相關(guān)的結(jié)論,我在這里總結(jié)出來,可以自己去試驗(yàn)一下。

  • 純swift類沒有動態(tài)性,但在?法、屬性前添加dynamic修飾,可獲得動態(tài)性。
  • 繼承?NSObject的swift類,其繼承??類的?法具有動態(tài)性,其它?定義?法、屬性想要獲得動態(tài)性,需要添加dynamic修飾。 
  • 若?法的參數(shù)、屬性類型為swift特有、?法映射到objective-c的類型(如Character、Tuple),則 此?法、屬性?法添加dynamic修飾(編譯器報錯)

Mirror

Mirror的基本用法

所謂反射就是可以動態(tài)獲取類型、成員信息,在運(yùn)?時可以調(diào)??法、屬性等?為的特性。在使?OC開發(fā)時很少強(qiáng)調(diào)其反射概念,因?yàn)镺C的Runtime要?其他語?中的反射強(qiáng)?的多。但是 Swift 是??類型安全的語?,不?持我們像 OC 那樣直接操作,它的標(biāo)準(zhǔn)庫仍然提供了反射機(jī)制來讓我們訪問成員信息,Swift 的反射機(jī)制是基于?個叫 Mirror 的結(jié)構(gòu)體來實(shí)現(xiàn)的。然后就可以通過它查詢這個實(shí)例。

Mirror的基本使用如下

class LGTeacher {
    var age:Int = 18
    func teach() {
        print("teach")
    }
}
//?先通過構(gòu)造?法構(gòu)建?個Mirror實(shí)例,這?傳?的參數(shù)是 Any,也就意味著當(dāng)前可以是類,結(jié)構(gòu)體,枚舉等
let mirror = Mirror(reflecting: LGTeacher())
//接下來遍歷 children 屬性,這是?個集合
for pro in mirror.children {
    //然后我們可以直接通過 label 輸出當(dāng)前的名稱,value 輸出當(dāng)前反射的值
    print("\(pro.label) : \(pro.value)")
}

Mirror的簡單應(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)
    }
}

可以看到,這?接受?個Any類型的參數(shù),同樣的這?有?個if case的寫法來判斷當(dāng)前的subject是否遵循了customReflectable協(xié)議,如果是我們就直接調(diào)?customMirror, 否則就進(jìn)?下級函數(shù)的調(diào)?。

這?有兩個需要注意的點(diǎn)if case的寫法,這?其實(shí)枚舉Case的模式匹配,和我們的Switch?樣,這?是只有?個case的 switch 語句。

于此同時這?出現(xiàn)了?個customRefletable的協(xié)議。我們來看一下它的用法。?先我們遵循 customReflectable 協(xié)議,并實(shí)現(xiàn)其中的屬性customMirror,customMirror會返回?個Mirror對象。代碼如下:

這里通過遵循customReflectable 協(xié)議并實(shí)現(xiàn)了其中的計(jì)算屬性customMirror,主要作用是當(dāng)我們使用lldb debug的時候,可以提供詳細(xì)的屬性信息。

我們接下來看如果不遵循customRefletable協(xié)議的類,那么就會走M(jìn)irror(internalReflecting: subject)代碼。

全局搜索internalReflecting,在ReflectionMirror.swift文件里面找到了這個方法的具體實(shí)現(xiàn)。

從代碼里面我們可以看到,首先需要獲取subject的真實(shí)類型信息。然后再獲取subject的屬性信息。 而獲取subject的真實(shí)類型信息則是通過_getNormalizedType這個方法來獲取的。搜索這個方法,然后我們就可以找到它的代碼。

這里使用了一個編譯器字段 @ silgen_name 其實(shí)是 Swift 的一個隱藏符號,作用是將某個 C/C++語言函數(shù)直接映射為 Swift 函數(shù)。也可以理解為為 C++ 代碼的 swift_reflectionMirror_normalizedType 函數(shù)定義一個在 swift 中使用的別名 _getNormalizedType。

所以調(diào)用了_getNormalizedType方法實(shí)際上是調(diào)用了swift_reflectionMirror_normalizedType方法,我在ReflectionMirror.cpp文件中找到了具體實(shí)現(xiàn)。

從代碼里面可以知道,通過call函數(shù)調(diào)用了ReflectionMirrorImpl類,然后返回這個類的類型。

我們先看一下ReflectionMirrorImpl類的具體內(nèi)容。

從注釋中,我們可以知道,這是一個抽象基類,也就是說不同的類型反射需要不同的類實(shí)現(xiàn)。

我們接下來看一下call函數(shù)的具體實(shí)現(xiàn)

在call函數(shù)中有一個Switch方法。根據(jù)不同的類型,調(diào)用不同的ReflectionMirrorImpl類。 我們就取EnumIpml類去探個究竟。

上面代碼就是EnumIpml類的具體實(shí)現(xiàn)。首先是isReflectable()這個方法。這個方法返回這個類型是否可以被反射,也就是找到metadata,再找到metadata中存儲的Description,通過它里面存儲的 isReflectable來確定。

接下來我們看一下getInfo方法。在代碼里面我們可以看到,獲取name、info屬性信息主要是通過getFieldAt來獲取。我們現(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};

這里可以看到可以看到 所有的信息都是通過Metadata、getDescription()、FieldDescrition 這幾個東西來去實(shí)現(xiàn)的,?個是當(dāng)前類型的元數(shù)據(jù),?個是當(dāng)前類型的描述,?個是對當(dāng)前類型屬性的描述。

Enum Metadata探索

我們在類和結(jié)構(gòu)體這篇文章里面,描述了類的Metadata結(jié)構(gòu),并把它的C++代碼轉(zhuǎn)換成了Swift代碼。我們這次來嘗試轉(zhuǎn)換Enum和struct的MetaData結(jié)構(gòu)。首先來探索Enum的Metadata結(jié)構(gòu)。

還原TargetEnumMetadata

通過源碼全局搜索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有一個屬性Kind,這個Kind主要是存儲MetadataKind類,是個int_32類型。TargetValueMetadata里面有Description屬性,因此我們可以把TargetEnumMetadata轉(zhuǎn)成這樣的結(jié)構(gòu)體

struct TargetEnumMetadata {
    var kind: Int
    var typeDescriptor: UnsafeRawPointer
}

還原TargetEnumDescriptor

接下來,我們要還原typeDescriptor的結(jié)構(gòu),雖然在TargetValueMetadata類中是TargetValueTypeDescriptor類,而我們在TargetMetadata發(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把它還原出來,還原的代碼如下:

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
}

此時TargetMetadata中的數(shù)據(jù)結(jié)構(gòu)也可以修改一下

struct TargetEnumMetadata{
    var kind: Int
    var typeDescriptor: UnsafeMutablePointer<TargetEnumDescriptor>
}

相對偏移指針

在上面的源碼中,我們可以發(fā)現(xiàn),TargetValueTypeDescriptor中的屬性、比如name、Fields等,他們的類型都是用TargetRelativeDirectPointer來定義。我們現(xiàn)在來看一下TargetRelativeDirectPointer是什么?

從上面的源碼定義可以知道,它是一個模板類,(接收三個參數(shù),?個是Runtime, ?個是Pointee , Bool類型默認(rèn)為True)。接下來我們看一下RelativeDirectPointer。

這個指針類代碼比較簡單,其中 T 就是我們進(jìn)來的類型,Offset就是int32_t 的類型,從字面意思上看應(yīng)該是偏移量之類的。我們再看下它的get()方法。

從以上代碼,我們可以看出TargetRelativeDirectPointer應(yīng)該是一個用來相對尋址的指針類。在 Swift中引??個實(shí)例對象有兩種情況:一種是直接尋址,另外一種是相對尋址。比如 TargetEnumDescriptor 中的 Name,這個 Name 存儲的值并不是 Name 表意上的值,Name存儲的是一個叫做相對偏移量或者叫偏移信息。此時,我們拿到 Name 的值的內(nèi)存地址做法是:Name 的內(nèi)存地址 + 相對偏移量。在 Swift 里面有很多這樣的偏移信息,這樣做可以節(jié)省內(nèi)存空間,避免存儲大量的內(nèi)存地址。

對此,我們把TargetRelativeDirectPointer給還原出來。還原代碼如下:

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

打印枚舉中的屬性

最后,我們來打印一下枚舉中的屬性。代碼如下:

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)在我們來解析一下struct類型的Metadata。

首先,和上面的Enum一樣,通過全局搜索,找到struct的MetaData是TargetStructMetadata類。通過它的繼承鏈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一樣,我們還原出來的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
    }

}

此時`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};
    }
}

我們來看一下getFields()方法,這個方法就是獲取 fields 的方法,fields 存的是 FieldRecords,通過getFieldRecordBuffer()來讀取FieldRecords。

在getFieldRecordBuffer()方法中,通過 reinterpret_cast 將 (this + 1) 強(qiáng)制轉(zhuǎn)換成 FieldRecord * 類型。所以我們可以推測這個 fields 是一塊連續(xù)的內(nèi)存空間,這一塊連續(xù)的內(nèi)存空間存儲的是 FieldRecord 類型,并且 NumFields 是它的容量大小。

在 C++ 中, this 是一個指向該對象的指針,由于它是一個指針,因此它可以應(yīng)用指針?biāo)阈g(shù)甚至數(shù)組索引。如果這個 this 是數(shù)組中的一個元素,(this + 1) 則將指向數(shù)組中的下一個對象。

所以我們可以把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我們可以把它還原成一個有連續(xù)空間的數(shù)組,容量為NumFields,存儲的是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)都還原出來。

獲取結(jié)構(gòu)體的屬性

現(xiàn)在我們來驗(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("----------開始解析---------------")
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("============開始解析屬性============")
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é)果
----------開始解析---------------
當(dāng)前結(jié)構(gòu)體的名稱: LGStudent**
當(dāng)前結(jié)構(gòu)體的屬性數(shù)量 3
============開始解析屬性============
--- 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ù)

這個函數(shù)的源碼在MetadataLookup.cpp文件中,我們來看一下它的具體實(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();
}

這個函數(shù)返回的是Metadata類型的指針,也就是Swift函數(shù)中的Type類型??梢酝ㄟ^這個函數(shù)獲取到每個函數(shù)的類型。但是這個函數(shù)是C++函數(shù),需要把它轉(zhuǎn)換成Swift函數(shù)。這里參考了HandyJSON這個第三方庫,使用@_silgen_name映射成swift函數(shù)。具體代碼如下:

@_silgen_name("swift_getTypeByMangledNameInContext")
func swift_H_getTypeByMangledNameInContext(typeName: UnsafeRawPointer, len: Int, context: UnsafeRawPointer, generic: UnsafeRawPointer) -> UnsafeRawPointer

獲取屬性的值

我們可以根據(jù)類型和 FieldOffsetVectorOffset屬性值存儲相對于實(shí)例的偏移量獲取屬性值,獲取屬性值存儲的指針。參考 HandyJSON 通過協(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
}
  • 這個函數(shù)傳入一個Any.Type的類型,通過它來創(chuàng)建一個協(xié)議的 Metadata結(jié)構(gòu)相同的BrigeProtocolMetadata實(shí)例
  • 通過 BrigeProtocolMetadata轉(zhuǎn)換成協(xié)議的 BrigeProtocol.Type.self,也就是協(xié)議的 Metadata,那么此時這個協(xié)議類型可以獲取到屬性的真實(shí)類型
  • 將屬性值指針轉(zhuǎn)換為 Self類型的類型指針,通過pointee就可以獲取真實(shí)的值

以上就是Swift進(jìn)階教程Mirror反射示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Swift進(jìn)階Mirror反射的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • swift中獲取字符串前綴的七種方法總結(jié)

    swift中獲取字符串前綴的七種方法總結(jié)

    在日常的開發(fā)中,經(jīng)常會需要獲取一個字符串的前綴,在這篇文章中我總結(jié)了在 Swift 中檢查字符串前綴的多種方法分享給大家,看看有沒有你不知道的
    2023-12-12
  • 使用?Swift?Package?插件生成代碼的示例詳解

    使用?Swift?Package?插件生成代碼的示例詳解

    這篇文章主要介紹了使用?Swift?Package?插件生成代碼,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • Swift教程之類與結(jié)構(gòu)詳解

    Swift教程之類與結(jié)構(gòu)詳解

    這篇文章主要介紹了Swift教程之類與結(jié)構(gòu)詳解,本文講解了類和結(jié)構(gòu)的異同、結(jié)構(gòu)和枚舉類型是數(shù)值類型、類是引用類型、如何選擇使用類還是結(jié)構(gòu)、集合類型的賦值和復(fù)制操作等內(nèi)容,需要的朋友可以參考下
    2015-01-01
  • 詳談swift內(nèi)存管理中的引用計(jì)數(shù)

    詳談swift內(nèi)存管理中的引用計(jì)數(shù)

    下面小編就為大家?guī)硪黄斦剆wift內(nèi)存管理中的引用計(jì)數(shù)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • swift中的@UIApplicationMain示例詳解

    swift中的@UIApplicationMain示例詳解

    這篇文章主要給大家介紹了關(guān)于swift中@UIApplicationMain的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • Swift3遷移至Swift4可能遇到的問題小結(jié)

    Swift3遷移至Swift4可能遇到的問題小結(jié)

    每當(dāng)看到新的編程語言我總是會有相當(dāng)大的興趣,所以下面這篇文章主要給大家介紹了關(guān)于Swift3遷移至Swift4可能遇到的問題,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-06-06
  • 深入理解Swift中的訪問控制關(guān)鍵字

    深入理解Swift中的訪問控制關(guān)鍵字

    這篇文章主要給大家介紹了Swift中訪問控制關(guān)鍵字的相關(guān)資料,文中介紹的非常詳細(xì),對大家具有一定的參考價值,需要的朋友們下面來一起看看吧。
    2017-03-03
  • Swift中的類class與結(jié)構(gòu)體struct體學(xué)習(xí)筆記

    Swift中的類class與結(jié)構(gòu)體struct體學(xué)習(xí)筆記

    和C++一樣,Swfit中同時擁有類與結(jié)構(gòu)體,能夠充分滿足開發(fā)者面向?qū)ο蠛兔嫦蜻^程編程的需求,這里我們就來看一下Swift中的類class與結(jié)構(gòu)體struct體學(xué)習(xí)筆記
    2016-07-07
  • 解析Swift語言面相對象編程中的繼承特性

    解析Swift語言面相對象編程中的繼承特性

    這篇文章主要介紹了解析Swift語言面相對象編程中的繼承特性,是Swift入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-11-11
  • Swift編程中的switch...case語句實(shí)例解析

    Swift編程中的switch...case語句實(shí)例解析

    這篇文章主要介紹了Swift編程中的switch...case語句實(shí)例解析,其中重點(diǎn)還是對于fallthrough關(guān)鍵字用法的講解,需要的朋友可以參考下
    2016-04-04

最新評論