Swift中 Class和Struct的底層結(jié)構(gòu)

該篇主要是關(guān)于研究Class和Struct的底層結(jié)構(gòu),以及Swift反射的相關(guān)知識(shí)。

1.Struct和Class的內(nèi)存分布

class Size {
    var width = 1
    var height = 2
}

struct Point {
    var x = 3
    var y = 4
}

眾所周知,結(jié)構(gòu)體的值是直接儲(chǔ)存在棾诔担空間,類的值是儲(chǔ)存在堆空間蒲每,椃柞耍空間是堆的地址指針。

類和結(jié)構(gòu)體內(nèi)存分布.jpg

那我們所研究的底層結(jié)構(gòu)又是什么邀杏?儲(chǔ)存在哪里贫奠?

2. 從Mirror的解析開始

說要獲取底層結(jié)構(gòu),不得不先介紹Mirror。不同于OC的動(dòng)態(tài)性質(zhì)唤崭,可以通過Runtime獲取到對象的屬性類型等信息拷恨。swift標(biāo)準(zhǔn)庫提供了反射機(jī)制,用來訪問成員信息谢肾,即Mirror腕侄。
Mirror反射是指可以動(dòng)態(tài)獲取類型、成員信息勒叠,在運(yùn)行時(shí)可以調(diào)用方法兜挨、屬性等行為的特性。

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

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

print("對象類型:\(mirror.subjectType)")
print("對象屬性個(gè)數(shù):\(mirror.children.count)")
print("對象的屬性及屬性值")
for child in mirror.children {
    print("\(child.label!)---\(child.value)")
}

通過Mirror眯分,我們可以更簡單的獲取到對象的屬性值拌汇。那系統(tǒng)是如何通過Mirror獲取對應(yīng)的屬性以及值的?
通過源碼分析弊决,我們可以看到我們所需要的信息都是從Mirror這個(gè)結(jié)構(gòu)體獲取的噪舀。在我看來,Mirror更像一個(gè)包裝類飘诗,因?yàn)樗b了更底層結(jié)構(gòu)的東西与倡。

public struct Mirror {
  public enum DisplayStyle {
    case `struct`, `class`, `enum`, tuple, optional, collection
    case dictionary, `set`
  }
    public let subjectType: Any.Type
    public let children: Children
    public let displayStyle: DisplayStyle?
    public var superclassMirror: Mirror? {
      return _makeSuperclassMirror()
    }

    // 初始化方法
    public init(reflecting subject: Any) {
    if case let customized as CustomReflectable = subject {
      self = customized.customMirror
    } else {
      self = Mirror(internalReflecting: subject)
    }
  }
}

Mirror的實(shí)現(xiàn)是由一部分Swift代碼加上另一部分C++代碼。很多代碼就不截取了昆稿,多了反而亂纺座,這邊主要講的是思路,具體解析文章可以參考這篇《Swift中的反射Mirror》溉潭。
Mirror內(nèi)部的初始化方法是通過實(shí)例(包括對象净响,結(jié)構(gòu)體等)創(chuàng)建的,獲取類型通過_getNormalizedType喳瓣,獲取count 通過_getChildCount馋贤。

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

比如獲取類型的底層實(shí)現(xiàn)來說配乓,其本質(zhì)就是獲取ReflectionMirrorImpl對應(yīng)的type(當(dāng)然惠毁,更下面還有一層犹芹,Impl通常也只作為一層包裝)仁讨。

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

ReflectionMirrorImpl 是一個(gè)“基類”,提供了一些基礎(chǔ)的屬性洞豁。ReflectionMirrorImpl有以下6個(gè)子類:

TupleImpl 元組的反射
StructImpl 結(jié)構(gòu)體的反射
EnumImpl 枚舉的反射
ClassImpl 類的反射
MetatypeImpl 元數(shù)據(jù)的反射
OpaqueImpl 不透明類型的反射

所以終究來說盐固,比如結(jié)構(gòu)體的Mirror獲取的是對應(yīng)的StructImpl的東西荒给。這邊我們也以結(jié)構(gòu)體為例刁卜,來講解結(jié)構(gòu)體反射的思路蛔趴。

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

 // 表明是結(jié)構(gòu)體
 char displayStyle() {
   return 's';
 }
 // 獲取屬性個(gè)數(shù)
 intptr_t count() {
   if (!isReflectable()) {
     return 0;
   }

   auto *Struct = static_cast<const StructMetadata *>(type);
   return Struct->getDescription()->NumFields;
 }
 // 獲取每個(gè)屬性的偏移值
 intptr_t childOffset(intptr_t i) {
   auto *Struct = static_cast<const StructMetadata *>(type);

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

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

 const FieldType childMetadata(intptr_t i, const char **outName,
                               void (**outFreeFunc)(const char *)) {
   StringRef name;
   FieldType fieldInfo;
   std::tie(name, fieldInfo) = getFieldAt(type, i);
   assert(!fieldInfo.isIndirect() && "indirect struct fields not implemented");
   
   *outName = name.data();
   *outFreeFunc = nullptr;
   
   return fieldInfo;
 }
 // 可以獲取到屬性名稱和屬性偏移的指針,也就是屬性值鱼蝉。
 AnyReturn subscript(intptr_t i, const char **outName,
                     void (**outFreeFunc)(const char *)) {
   auto fieldInfo = childMetadata(i, outName, outFreeFunc);

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

   return copyFieldContents(fieldData, fieldInfo);
 }
};

首先一個(gè)判斷是否支持反射的方法箫荡,最中是訪問的Description->isReflectable()。
isReflectable 的實(shí)現(xiàn)來看洁奈,我們可以看出最根本的數(shù)據(jù)結(jié)構(gòu)StructMetadata绞灼。獲取屬性個(gè)數(shù)和偏移值也是同樣的思路。而且都是通過Struct->getDescription()獲取到Description所獲取的印叁,關(guān)于Description有一系列繼承關(guān)系军掂,這邊我就不一一解釋良姆,我們可以從下圖的繼承鏈中幔戏,看出Description存儲(chǔ)著很多信息(基本包括所有我們想要的信息)。

StructDescriptor繼承鏈.png

類型名稱痊剖,屬性數(shù)量垒玲,屬性偏移量這些都是比較常用的,我們重點(diǎn)講解Fields叮贩。
Fields是一個(gè)指針,指向一個(gè)FieldDescriptor彪蓬。類似Description捺萌,F(xiàn)ieldDescriptor存儲(chǔ)著屬性的相關(guān)信息。仿寫的結(jié)構(gòu)體如下:

struct FieldDescriptor {

    enum FieldDescriptorKind: UInt16 {
        case Struct
        case Class
        case Enum
        // Fixed-size multi-payload enums have a special descriptor format that encodes spare bits.
        case MultiPayloadEnum
        // A Swift opaque protocol. There are no fields, just a record for the type itself.
        case kProtocol
        // A Swift class-bound protocol.
        case ClassProtocol
        // An Objective-C protocol, which may be imported or defined in Swift.
        case ObjCProtocol
        // An Objective-C class, which may be imported or defined in Swift.
        // In the former case, field type metadata is not emitted, and must be obtained from the Objective-C runtime.
        case ObjCClass
    }

    var MangledTypeName: RelativeDirectPointer<CChar>//類型命名重整
    var Superclass: RelativeDirectPointer<CChar>//父類名
    var Kind: FieldDescriptorKind//類型酷誓,看枚舉
    var FieldRecordSize: Int16 //這個(gè)值乘上NumFields會(huì)拿到RecordSize
    var NumFields: Int32//還是屬性個(gè)數(shù)

    //獲取每個(gè)屬性态坦,得到FieldRecord
    mutating func getField(index: Int) -> UnsafeMutablePointer<FieldRecord> {
        return withUnsafeMutablePointer(to: &self) {
            let arrayPtr = UnsafeMutableRawPointer($0.advanced(by: 1)).assumingMemoryBound(to: FieldRecord.self)
            return arrayPtr.advanced(by: index)
        }
    }
}

FieldRecord是每個(gè)屬性的信息,包含屬性名娘扩,屬性的性質(zhì)壮锻。通過Index,從FieldDescriptor獲取到對應(yīng)的屬性信息猜绣。那屬性值哪里獲取呢掰邢?按照我的理解,結(jié)構(gòu)體的元數(shù)據(jù)是存儲(chǔ)在堆上的掰伸,用于創(chuàng)建實(shí)例怀估,實(shí)例的溯源狮鸭。屬性值是不固定的歧蕉,屬于實(shí)例的信息康铭,存儲(chǔ)在棧上的。所以屬性值得另外通過偏移值去獲取催跪。

struct FieldRecord {
    struct FieldRecordFlags {
        var Data: UInt32
        /// Is this an indirect enum case?
        func isIndirectCase() -> Bool {
            return (Data & 0x1) == 0x1;
        }

        /// Is this a mutable `var` property?
        func isVar() -> Bool {
            return (Data & 0x2) == 0x2;
        }
    }

    var Flags: FieldRecordFlags //標(biāo)記位
    var MangledTypeName: RelativeDirectPointer<CChar>//類型命名重整
    var FieldName: RelativeDirectPointer<CChar>//屬性名
}

具體怎么獲取屬性的值呢?有如下步驟:(具體代碼有空再補(bǔ)上匿沛,這段也是抄的哈哈)

  • 首先獲取FieldOffsetVectorOffset的值
  • 然后在加上this也就是當(dāng)前Metadata的指針
  • 這里我們將仿寫的StructMetadata的指針ptr重綁定為Int
  • 源碼中加上FieldOffsetVectorOffset逃呼,這里我們就移動(dòng)FieldOffsetVectorOffset
  • 然后將上述移動(dòng)后的綁定為一個(gè)Int32的指針
  • 最后使用UnsafeBufferPointer和屬性個(gè)數(shù)創(chuàng)建一個(gè)buffer數(shù)組指針
  • 接下來我們就可以從數(shù)組中取出每個(gè)屬性的偏移值
  • 然后取出結(jié)構(gòu)體實(shí)例p的內(nèi)存地址
  • 然后按照buffer數(shù)組中的偏移值進(jìn)行偏移者娱,重綁定為屬性的類型
  • 最后就可以打印出屬性值了

所以,我們可以總結(jié)一下:
Mirror是一個(gè)包裝類推姻,通過傳入的對象類型獲取對應(yīng)的ReflectionMirrorImpl框沟,比如結(jié)構(gòu)體對應(yīng)的Impl類型為StructImpl,StructImpl會(huì)在內(nèi)部創(chuàng)建一個(gè)StructMetadata拧晕。Mirror獲取屬性值都是間接從StructMetadata獲取梅垄,屬性名队丝,屬性類型這一類固定的信息可以直接獲取,屬性值是通過StructMetadata獲取的屬性偏移地址獲取的臭墨。

Mirror解析流程.png

3. Struct的底層結(jié)構(gòu)

在上面Mirror的解析中膘盖,已經(jīng)對Struct的底層結(jié)構(gòu)有了進(jìn)一步研究。
很明顯,再概括性的解釋下StructMetaData践图,Struct的底層結(jié)構(gòu)就清晰可見了沉馆。

如上一部分Mirror解析所說的,Mirror底層的核心邏輯揖盘,就是通過getKind()方法獲取該類型的元數(shù)據(jù)類型兽狭,然后根據(jù)該類型Metadata獲取相應(yīng)的屬性,比如類型名稱服球、屬性名字颠焦,屬性個(gè)數(shù)等。


MetadataKind.png

每種類型都有對應(yīng)的Metadata粉渠,所以研究Struct或者Class都是通過對應(yīng)的Metadata來研究的霸株。


MetadataKind對應(yīng)表.png

通過對StructMetadata源碼的解析盯捌,我們可以概括(仿寫)下StructMetadata的結(jié)構(gòu)饺著,這樣便于理解。

struct StructMetadata {
    var Kind: InProcess   // MetadataKind靴跛,結(jié)構(gòu)體的枚舉值是0x200
    var Description: UnsafeMutablePointer<TargetStructDescriptor>// 結(jié)構(gòu)體的描述渡嚣,包含了結(jié)構(gòu)體的所有信息识椰,是一個(gè)指針
    
    //獲得每個(gè)屬性的在結(jié)構(gòu)體中內(nèi)存的起始位置、
    mutating func getFieldOffset(index: Int) -> Int {
        if Description.pointee.NumFields == 0 {
            print("結(jié)構(gòu)體沒有屬性")
            return 0
        }
        let fieldOffsetVectorOffset = self.Description.pointee.FieldOffsetVectorOffset
        return withUnsafeMutablePointer(to: &self) {
            //獲得自己本身的起始位置
            let selfPtr = UnsafeMutableRawPointer($0).assumingMemoryBound(to: InProcess.self)
            //以指針的步長偏移FieldOffsetVectorOffset
            let fieldOffsetVectorOffsetPtr = selfPtr.advanced(by: numericCast(fieldOffsetVectorOffset))
            //屬性的起始偏移量已32位整形存儲(chǔ)的藏畅,轉(zhuǎn)一下指針
            let tramsformPtr = UnsafeMutableRawPointer(fieldOffsetVectorOffsetPtr).assumingMemoryBound(to: UInt32.self)
            return numericCast(tramsformPtr.advanced(by: index).pointee)
        }
    }
}

可以看出StructMetadata主要由Kind和Description組成功咒,Kind用來表明自己是一個(gè)Struct的Metadata。Description則隱藏著Struct的各種信息幽七。
那Description的具體結(jié)構(gòu)又有什么溅呢?仿寫的TargetStructDescriptor結(jié)構(gòu)如下:

struct TargetStructDescriptor {
    // 存儲(chǔ)在任何上下文描述符的第一個(gè)公共標(biāo)記
    var Flags: ContextDescriptorFlags

    // 復(fù)用的RelativeDirectPointer這個(gè)類型藕届,其實(shí)并不是,但看下來原理一樣
    // 父級上下文梁厉,如果是頂級上下文則為null踏兜。獲得的類型為InProcess,里面存放的應(yīng)該是一個(gè)指針肉盹,測下來結(jié)構(gòu)體里為0疹尾,相當(dāng)于null了
    var Parent: RelativeDirectPointer<InProcess>

    // 獲取Struct的名稱
    var Name: RelativeDirectPointer<CChar>

    // 這里的函數(shù)類型是一個(gè)替身纳本,需要調(diào)用getAccessFunction()拿到真正的函數(shù)指針(這里沒有封裝),會(huì)得到一個(gè)MetadataAccessFunction元數(shù)據(jù)訪問函數(shù)的指針的包裝器類吓笙,該函數(shù)提供operator()重載以使用正確的調(diào)用約定來調(diào)用它(可變長參數(shù))巾腕,意外發(fā)現(xiàn)命名重整會(huì)調(diào)用這邊的方法(目前不太了解這塊內(nèi)容)尊搬。
    var AccessFunctionPtr: RelativeDirectPointer<UnsafeRawPointer>

    // 一個(gè)指向類型的字段描述符的指針(如果有的話)。類型字段的描述幌墓,可以從里面獲取結(jié)構(gòu)體的屬性。
    var Fields: RelativeDirectPointer<FieldDescriptor>
    // 結(jié)構(gòu)體屬性個(gè)數(shù)
    var NumFields: Int32
    // 存儲(chǔ)這個(gè)結(jié)構(gòu)的字段偏移向量的偏移量(記錄你屬性起始位置的開始的一個(gè)相對于metadata的偏移量,具體看metadata的getFieldOffsets方法)袭祟,如果為0捞附,說明你沒有屬性
    var FieldOffsetVectorOffset: Int32
}

//這個(gè)類型是通過當(dāng)前地址的偏移值獲得真正的地址鸟召,有點(diǎn)像文件目錄,用當(dāng)前路徑的相對路徑獲得絕對路徑压状。
struct RelativeDirectPointer<T> {
    var offset: Int32 //存放的與當(dāng)前地址的偏移值

    //通過地址的相對偏移值獲得真正的地址
    mutating func get() -> UnsafeMutablePointer<T> {
        let offset = self.offset
        return withUnsafeMutablePointer(to: &self) {
            return UnsafeMutableRawPointer($0).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self)
        }
    }
}

舉個(gè)例子??:

struct Teacher: Codable {
    var name = "Tom"
    var age = 23
    var city = "ShangHai"
    var height = 175
}

// 通過源碼我們可以知道Type類型對應(yīng)的就是Metadata跟继,這里記住要轉(zhuǎn)成Any.Type舔糖,不然typesize不一致,不讓轉(zhuǎn)
let ptr = unsafeBitCast(Teacher.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)
print("0x\(String(ptr.pointee.Kind.PointerSize, radix: 16))") //kind枚舉值是0x200十兢,代表著結(jié)構(gòu)體

let descriptionptr = ptr.pointee.Description

let Flags = descriptionptr.pointee.Flags
print(Flags.getContextDescriptorKind()!)  // 公共標(biāo)記中獲取kind為Struct

let ParentPtr = descriptionptr.pointee.Parent.get()
print(ParentPtr.pointee.PointerSize) // 結(jié)果為0旱物,說明已經(jīng)是頂級上下文了

let structName = descriptionptr.pointee.Name.get()
print(String(cString: structName)) // 拿到Teacher字符串

//拿到屬性個(gè)數(shù)跟匆,屬性名字,屬性在內(nèi)存的起始位置烤蜕,這樣就可以取值迹冤,mirror的原理就是這個(gè)E葆恪!
let propertyCount = Int(descriptionptr.pointee.NumFields)
print("屬性個(gè)數(shù):\(propertyCount)")
print("---------")
(0..<propertyCount).forEach {
    let propertyPtr = descriptionptr.pointee.Fields.get().pointee.getField(index: $0)
    print("""
        屬性名:\(String(cString: propertyPtr.pointee.FieldName.get()))
        起始位置:\(ptr.pointee.getFieldOffset(index: $0))
        類型命名重整:\(String(cString: propertyPtr.pointee.MangledTypeName.get()))
        是否是var修飾的變量:\(propertyPtr.pointee.Flags.isVar() ? "是" : "否" )
        ---------
        """)
}

4.Class的底層結(jié)構(gòu)

類似StructMetaData莉兰,Class的底層結(jié)構(gòu)是ClassMetadata糖荒。當(dāng)然不是這么一個(gè)簡單的結(jié)構(gòu),是有層層繼承鏈的(TargetClassMetadata蜘矢,TargetAnyClassMetadata)综看。

ClassMetaData繼承鏈.png

不同層有著不同的屬性红碑。所以這邊不打算理清它們的繼承關(guān)系,簡單地總結(jié)下ClassMetadata的相關(guān)屬性镣典。

// Swift中的class的metadata兼容OC的類
struct ClassMetadata {
    // 在oc中放的就是isa唾琼,在swift中kind大于0x7FF表示的就是類
    var Kind: InProcess
    // 父類的Metadata锡溯,如果是null說明是最頂級的類了
    var Superclass: UnsafeMutablePointer<ClassMetadata>
    // 緩存數(shù)據(jù)用于某些動(dòng)態(tài)查找,它由運(yùn)行時(shí)擁有芜茵,通常需要與Objective-C的使用進(jìn)行互操作倡蝙。(說到底就是OC的東西)
    var CacheData1: UnsafeMutablePointer<UnsafeRawPointer>
    var CacheData2: UnsafeMutablePointer<UnsafeRawPointer>
    // 除了編譯器設(shè)置低位以表明這是Swift元類型外寺鸥,這個(gè)data里存的指針,用于行外元數(shù)據(jù)烤低,通常是不透明的(應(yīng)該也是OC的)
    var Data: InProcess  
    // Swift-specific class flags.
    var Flags: ClassFlags
    // The address point of instances of this type.
    var InstanceAddressPoint: UInt32
    // The required size of instances of this type.(實(shí)例對象在堆內(nèi)存的大小)
    var InstanceSize: UInt32
    // The alignment mask of the address point of instances of this type. (根據(jù)這個(gè)mask來獲取內(nèi)存中的對齊大小)
    var InstanceAlignMask: UInt16
    // Reserved for runtime use.(預(yù)留給運(yùn)行時(shí)使用)
    var Reserved: UInt16
    // The total size of the class object, including prefix and suffix extents.
    var ClassSize: UInt32
    // The offset of the address point within the class object.
    var ClassAddressPoint: UInt32
    // 一個(gè)對類型的超行的swift特定描述扑馁,如果這是一個(gè)人工子類,則為null复罐。目前不提供動(dòng)態(tài)創(chuàng)建非人工子類的機(jī)制雄家。
    var Description: UnsafeMutablePointer<TargetClassDescriptor>
    // 銷毀實(shí)例變量的函數(shù)咳短,用于在構(gòu)造函數(shù)早期返回后進(jìn)行清理蛛淋。如果為null褐荷,則不會(huì)執(zhí)行清理操作,并且所有的ivars都必須是簡單的层宫。
    var IVarDestroyer: UnsafeMutablePointer<ClassIVarDestroyer>
}

對比StructMeteData其监,ClassMetadata多了很多屬性抖苦。

// Swift中的class的metadata兼容OC的類
struct ClassMetadata {
    // 在oc中放的就是isa,在swift中kind大于0x7FF表示的就是類
    var Kind: InProcess
    // 父類的Metadata贮庞,如果是null說明是最頂級的類了
    var Superclass: UnsafeMutablePointer<ClassMetadata>
    // 緩存數(shù)據(jù)用于某些動(dòng)態(tài)查找究西,它由運(yùn)行時(shí)擁有卤材,通常需要與Objective-C的使用進(jìn)行互操作。(說到底就是OC的東西)
    var CacheData1: UnsafeMutablePointer<UnsafeRawPointer>
    var CacheData2: UnsafeMutablePointer<UnsafeRawPointer>
    // 除了編譯器設(shè)置低位以表明這是Swift元類型外伏伐,這個(gè)data里存的指針晕拆,用于行外元數(shù)據(jù),通常是不透明的(應(yīng)該也是OC的)
    var Data: InProcess
    
    // 該對象是否是有效的swift類型元數(shù)據(jù)? 也就是說堤器,它可以安全地向下轉(zhuǎn)換到類元數(shù)據(jù)(ClassMetadata)嗎?
    func isTypeMetadata() -> Bool {
        return ((Data & 2) != 0)
    }
    
    func isPureObjC() -> Bool {
        return !isTypeMetadata()
    }
    
    /**
     源碼中
     上面的字段都是TargetAnyClassMetadata父類的末贾,類元數(shù)據(jù)對象中與所有類兼容的部分拱撵,即使是非swift類。
     下面的字段都是TargetClassMetadata的乓旗,只有在isTypeMetadata()時(shí)才有效集索,所以在源碼在使用時(shí)都比較小心务荆,會(huì)經(jīng)常調(diào)用isTypeMetadata()
     Objective-C運(yùn)行時(shí)知道下面字段的偏移量
     */
    
    // Swift-specific class flags.
    var Flags: ClassFlags
    // The address point of instances of this type.
    var InstanceAddressPoint: UInt32
    // The required size of instances of this type.(實(shí)例對象在堆內(nèi)存的大小)
    var InstanceSize: UInt32
    // The alignment mask of the address point of instances of this type. (根據(jù)這個(gè)mask來獲取內(nèi)存中的對齊大小)
    var InstanceAlignMask: UInt16
    // Reserved for runtime use.(預(yù)留給運(yùn)行時(shí)使用)
    var Reserved: UInt16
    // The total size of the class object, including prefix and suffix extents.
    var ClassSize: UInt32
    // The offset of the address point within the class object.
    var ClassAddressPoint: UInt32
    // 一個(gè)對類型的超行的swift特定描述,如果這是一個(gè)人工子類娱据,則為null吸耿。目前不提供動(dòng)態(tài)創(chuàng)建非人工子類的機(jī)制酷窥。
    var Description: UnsafeMutablePointer<TargetClassDescriptor>
    // 銷毀實(shí)例變量的函數(shù)蓬推,用于在構(gòu)造函數(shù)早期返回后進(jìn)行清理。如果為null糕珊,則不會(huì)執(zhí)行清理操作毅糟,并且所有的ivars都必須是簡單的。
    var IVarDestroyer: UnsafeMutablePointer<ClassIVarDestroyer>
    
    
    //獲得每個(gè)屬性的在結(jié)構(gòu)體中內(nèi)存的起始位置
    mutating func getFieldOffset(index: Int) -> Int {
        if Description.pointee.NumFields == 0 || Description.pointee.FieldOffsetVectorOffset == 0 {
            print("沒有屬性")
            return 0
        }
        let fieldOffsetVectorOffset = self.Description.pointee.FieldOffsetVectorOffset
        return withUnsafeMutablePointer(to: &self) {
            //獲得自己本身的起始位置
            let selfPtr = UnsafeMutableRawPointer($0).assumingMemoryBound(to: InProcess.self)
            //以指針的步長偏移FieldOffsetVectorOffset
            let fieldOffsetVectorOffsetPtr = selfPtr.advanced(by: numericCast(fieldOffsetVectorOffset))
            //屬性的起始偏移量已32位整形存儲(chǔ)的喇肋,轉(zhuǎn)一下指針
            let tramsformPtr = UnsafeMutableRawPointer(fieldOffsetVectorOffsetPtr).assumingMemoryBound(to: InProcess.self)
            return numericCast(tramsformPtr.advanced(by: index).pointee)
        }
    }
}

除了Struct有的一些屬性外,多了一些適配OC的屬性甚侣。當(dāng)然存儲(chǔ)的屬性信息也是放在Description间学。Description中的FieldRecord和Struct的一樣低葫,我就不貼出來了。

struct TargetClassDescriptor {
    // 存儲(chǔ)在任何上下文描述符的第一個(gè)公共標(biāo)記
    var Flags: ContextDescriptorFlags

    // 復(fù)用的RelativeDirectPointer這個(gè)類型殷绍,其實(shí)并不是鹊漠,但看下來原理一樣
    // 父級上下文躯概,如果是頂級上下文則為null畔师。
    var Parent: RelativeDirectPointer<InProcess>

    // 獲取類的名稱
    var Name: RelativeDirectPointer<CChar>

    // 這里的函數(shù)類型是一個(gè)替身看锉,需要調(diào)用getAccessFunction()拿到真正的函數(shù)指針(這里沒有封裝),會(huì)得到一個(gè)MetadataAccessFunction元數(shù)據(jù)訪問函數(shù)的指針的包裝器類呻此,該函數(shù)提供operator()重載以使用正確的調(diào)用約定來調(diào)用它(可變長參數(shù))焚鲜,意外發(fā)現(xiàn)命名重整會(huì)調(diào)用這邊的方法(目前不太了解這塊內(nèi)容)放前。
    var AccessFunctionPtr: RelativeDirectPointer<UnsafeRawPointer>

    // 一個(gè)指向類型的字段描述符的指針(如果有的話)凭语。類型字段的描述,可以從里面獲取結(jié)構(gòu)體的屬性吨些。
    var Fields: RelativeDirectPointer<FieldDescriptor>
    
    // The type of the superclass, expressed as a mangled type name that can refer to the generic arguments of the subclass type.
    var SuperclassType: RelativeDirectPointer<CChar>
    
    // 下面兩個(gè)屬性在源碼中是union類型,所以取size大的類型作為屬性(這里貌似一樣)挽拔,具體還得判斷是否have a resilient superclass
    
    // 有resilient superclass螃诅,用ResilientMetadataBounds状囱,表示對保存元數(shù)據(jù)擴(kuò)展的緩存的引用
    var ResilientMetadataBounds: RelativeDirectPointer<TargetStoredClassMetadataBounds>
    // 沒有resilient superclass使用MetadataNegativeSizeInWords亭枷,表示該類元數(shù)據(jù)對象的負(fù)大小(用字節(jié)表示)
    var MetadataNegativeSizeInWords: UInt32 {
        get {
            return UInt32(ResilientMetadataBounds.offset)
        }
    }

    // 有resilient superclass,用ExtraClassFlags猾编,表示一個(gè)Objective-C彈性類存根的存在
    var ExtraClassFlags: ExtraClassDescriptorFlags
    // 沒有resilient superclass使用MetadataPositiveSizeInWords升敲,表示該類元數(shù)據(jù)對象的正大小(用字節(jié)表示)
    var MetadataPositiveSizeInWords: UInt32 {
        get {
            return ExtraClassFlags.Bits
        }
    }
    /**
     此類添加到類元數(shù)據(jù)的其他成員的數(shù)目驴党。默認(rèn)情況下,這些數(shù)據(jù)對運(yùn)行時(shí)是不透明的倔既,而不是在其他成員中公開;它實(shí)際上只是NumImmediateMembers * sizeof(void*)字節(jié)的數(shù)據(jù)渤涌。
     這些字節(jié)是添加在地址點(diǎn)之前還是之后把还,取決于areImmediateMembersNegative()方法笨篷。
     */
    var NumImmediateMembers: UInt32
    
    // 屬性個(gè)數(shù),不包含父類的
    var NumFields: Int32
    // 存儲(chǔ)這個(gè)結(jié)構(gòu)的字段偏移向量的偏移量(記錄你屬性起始位置的開始的一個(gè)相對于metadata的偏移量练俐,具體看metadata的getFieldOffsets方法)冕臭,如果為0,說明你沒有屬性
    // 如果這個(gè)類含有一個(gè)彈性的父類归形,那么從他的彈性父類的metaData開始偏移
    var FieldOffsetVectorOffset: Int32
}

還有一點(diǎn)需要注意的是鼻由,ClassMetadata是在堆中的數(shù)據(jù)蕉世,類似OC中的元數(shù)據(jù)。如剛開始所比對的奸例,結(jié)構(gòu)體和類實(shí)例在內(nèi)存地址的不同表現(xiàn)向楼。類對象在棧中只是一個(gè)指針湖蜕,數(shù)據(jù)存儲(chǔ)在堆中,而ClassMetadata就是堆中數(shù)據(jù)頭部的指針,所謂的指向類型信息戈鲁。

所以以下我們通過創(chuàng)建對象的源碼來探索對象實(shí)例的本質(zhì)嘹叫。swift_allocObject的源碼如下:

// requiredSize是分配的實(shí)際內(nèi)存大小,為40
// requiredAlignmentMask是swift中的字節(jié)對齊方式罩扇,這個(gè)和OC中是一樣的喂饥,必須是8的倍數(shù),不足的會(huì)自動(dòng)補(bǔ)齊或粮,目的是以空間換時(shí)間捞高,來提高內(nèi)存操作效率袋毙。
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
                                       size_t requiredSize,
                                       size_t requiredAlignmentMask) {
  assert(isAlignmentMask(requiredAlignmentMask));
  // 通過swift_slowAlloc分配內(nèi)存听盖,并進(jìn)行內(nèi)存字節(jié)對齊
  auto object = reinterpret_cast<HeapObject *>(
      swift_slowAlloc(requiredSize, requiredAlignmentMask));//分配內(nèi)存+字節(jié)對齊

  // NOTE: this relies on the C++17 guaranteed semantics of no null-pointer
  // check on the placement new allocator which we have observed on Windows,
  // Linux, and macOS.
  new (object) HeapObject(metadata);//初始化一個(gè)實(shí)例對象

  // If leak tracking is enabled, start tracking this object.
  SWIFT_LEAKS_START_TRACKING_OBJECT(object);

  SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);

  return object;
}

通過new + HeapObject + metadata初始化一個(gè)實(shí)例對象生闲。函數(shù)的返回值是HeapObject類型,所以當(dāng)前對象的內(nèi)存結(jié)構(gòu)就是HeapObject的內(nèi)存結(jié)構(gòu)悬蔽。
進(jìn)入swift_slowAlloc函數(shù)捉兴,其內(nèi)部主要是通過malloc在堆中分配size大小的內(nèi)存空間倍啥,并返回內(nèi)存地址虽缕,主要是用于存儲(chǔ)實(shí)例變量。

void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
  void *p;
  // This check also forces "default" alignment to use AlignedAlloc.
  if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__)
    p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
    p = malloc(size);// 堆中創(chuàng)建size大小的內(nèi)存空間伍派,用于存儲(chǔ)實(shí)例變量
#endif
  } else {
    size_t alignment = (alignMask == ~(size_t(0)))
                           ? _swift_MinAllocationAlignment
                           : alignMask + 1;
    p = AlignedAlloc(size, alignment);
  }
  if (!p) swift::crash("Could not allocate memory.");
  return p;
}

通過查看HeapObject初始化方法诉植,我們可以看到需要兩個(gè)參數(shù):metadata昵观、refCounts。其中metadata類型是HeapMetadata灼擂,是一個(gè)指針類型缤至,占8字節(jié),后面我會(huì)講它怎么和ClassMetadata扯上關(guān)系嫉到。
refCounts(引用計(jì)數(shù)何恶,類型是InlineRefCounts嚼黔,而InlineRefCounts是一個(gè)類RefCounts的別名唬涧,占8個(gè)字節(jié))疫赎,swift采用arc引用計(jì)數(shù)。

所以總結(jié)起來碎节,實(shí)例對象在OC和Swift的區(qū)別是:

  • OC中實(shí)例對象的本質(zhì)是結(jié)構(gòu)體捧搞,是以objc_object為模板繼承的,其中有一個(gè)isa指針狮荔,占8字節(jié)胎撇。
  • Swift中實(shí)例對象,本質(zhì)也是結(jié)構(gòu)體(HeapObject)殖氏,默認(rèn)的比OC中多了一個(gè)refCounted引用計(jì)數(shù)大小,默認(rèn)屬性占16字節(jié)(metadata 8字節(jié) + refCounts 8字節(jié))雅采。

再接著上面的研究HeapMetadata和ClassMetadata的關(guān)系爵憎。
HeapMetadata中有個(gè)Kind,在getClassObject中去匹配kind返回值是TargetClassMetadata類型婚瓜。如果是Class宝鼓,則直接對this(當(dāng)前指針,即metadata)強(qiáng)轉(zhuǎn)為ClassMetadata闰渔。

 const TargetClassMetadata<Runtime> *getClassObject() const;
 
//******** 具體實(shí)現(xiàn) ********
template<> inline const ClassMetadata *
  Metadata::getClassObject() const {
    //匹配kind
    switch (getKind()) {
      //如果kind是class
    case MetadataKind::Class: {
      // Native Swift class metadata is also the class object.
      //將當(dāng)前指針強(qiáng)轉(zhuǎn)為ClassMetadata類型
      return static_cast<const ClassMetadata *>(this);
    }
    case MetadataKind::ObjCClassWrapper: {
      // Objective-C class objects are referenced by their Swift metadata wrapper.
      auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
      return wrapper->Class;
    }
    // Other kinds of types don't have class objects.
    default:
      return nullptr;
    }
  }

參考資料或博客
1.《Swift中進(jìn)階合集》
2.《初探Swift底層Metadata》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末席函,一起剝皮案震驚了整個(gè)濱河市铐望,隨后出現(xiàn)的幾起案子冈涧,更是在濱河造成了極大的恐慌,老刑警劉巖正蛙,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件督弓,死亡現(xiàn)場離奇詭異,居然都是意外死亡乒验,警方通過查閱死者的電腦和手機(jī)愚隧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锻全,“玉大人狂塘,你說我怎么就攤上這事录煤。” “怎么了荞胡?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵妈踊,是天一觀的道長。 經(jīng)常有香客問我泪漂,道長廊营,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任萝勤,我火速辦了婚禮露筒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘敌卓。我一直安慰自己慎式,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布假哎。 她就那樣靜靜地躺著瞬捕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪舵抹。 梳的紋絲不亂的頭發(fā)上肪虎,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機(jī)與錄音惧蛹,去河邊找鬼扇救。 笑死,一個(gè)胖子當(dāng)著我的面吹牛香嗓,可吹牛的內(nèi)容都是我干的迅腔。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼并徘!你這毒婦竟也來了耘眨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤锌雀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后迅诬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腋逆,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年侈贷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惩歉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖撑蚌,靈堂內(nèi)的尸體忽然破棺而出上遥,到底是詐尸還是另有隱情,我是刑警寧澤争涌,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布露该,位于F島的核電站,受9級特大地震影響第煮,放射性物質(zhì)發(fā)生泄漏解幼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一包警、第九天 我趴在偏房一處隱蔽的房頂上張望撵摆。 院中可真熱鬧,春花似錦害晦、人聲如沸特铝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鲫剿。三九已至,卻和暖如春稻轨,著一層夾襖步出監(jiān)牢的瞬間灵莲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工殴俱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留政冻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓线欲,卻偏偏與公主長得像明场,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子李丰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容