美文网首页
Swift底层原理-类与对象

Swift底层原理-类与对象

作者: 祀梦_ | 来源:发表于2022-12-07 15:58 被阅读0次

    Swift底层原理-类与对象

    准备工作

    • 该系列文章,主要通过Swift底层源码进行研究。
    • 可以通过该网址下载Swift源码Swift源码

    对象的创建

    • 对下面的代码进行断点调试,查看汇编
    class Test {
        var age: Int = 18
        var name: String = "ssl"
    }
    
    var t = Test()
    
    • 汇编结果如下
    image image
    • 最终调用底层的swift_allocObject方法,接下来我们具体看看该方法做了些什么。
    HeapObject *swift::swift_allocObject(HeapMetadata const *metadata,
                                         size_t requiredSize,
                                         size_t requiredAlignmentMask) {
      CALL_IMPL(swift_allocObject, (metadata, requiredSize, requiredAlignmentMask));
    }
    
    • 该方法内部调用了_swift_allocObject_方法
    static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
                                           size_t requiredSize,
                                           size_t requiredAlignmentMask) {
      assert(isAlignmentMask(requiredAlignmentMask));
      auto object = reinterpret_cast<HeapObject *>(
          swift_slowAlloc(requiredSize, requiredAlignmentMask));
    
      // 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);
    
      // If leak tracking is enabled, start tracking this object.
      SWIFT_LEAKS_START_TRACKING_OBJECT(object);
    
      SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
    
      return object;
    }
    
    • 内部会调用swift_slowAlloc方法,我们看一下该方法的实现
    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);
    #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;
    }
    
    • 该方法最终会去调用malloc去分配内存

    总结

    • Swift对象创建流程
      • swift_allocObject --> _swift_allocObject_ --> swift_slowAlloc --> malloc

    类的结构

    • 我们从对象的创建流程可知,最终返回的对象类型是HeapObject *。可以猜测所有类都是该类型结构。我们接下来看一下类的结构

    HeapObject

    #define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS       \
      InlineRefCounts refCounts
    
    /// The Swift heap-object header.
    /// This must match RefCountedStructTy in IRGen.
    struct HeapObject {
        /// This is always a valid pointer to a metadata object.
        HeapMetadata const *metadata;
    
        SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
        
        // 省略初始化方法
    };
    
    • 可以看到该结构体里面,只有两个属性。一个是metadata,另一个是refCounts,默认占用16字节。

    • 通过名称可以猜到,refCounts是引用计数,和内存管理相关。所以为了研究类的结构,重点就放在metadata上面

    HeapMetadata

    using HeapMetadata = TargetHeapMetadata<InProcess>;
    
    • HeapMetadataTargetHeapMetadata这个类型的别名,点击进入TargetHeapMetadata结构
    template <typename Runtime>
    struct TargetHeapMetadata : TargetMetadata<Runtime> {
      using HeaderType = TargetHeapMetadataHeader<Runtime>;
    
      TargetHeapMetadata() = default;
      constexpr TargetHeapMetadata(MetadataKind kind)
        : TargetMetadata<Runtime>(kind) {}
    #if SWIFT_OBJC_INTEROP
      constexpr TargetHeapMetadata(TargetAnyClassMetadata<Runtime> *isa)
        : TargetMetadata<Runtime>(isa) {}
    #endif
    };
    
    • 根据代码可知,该类继承TargetMetadata
    • 在这里有对 OCSwift 做兼容。如果是一个纯Swift类,初始化传入了MetadataKind;如果和OC交互,它就传入了一个isa

    MetadataKind

    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"
      
      LastEnumerated = 0x7FF,
    };
    
    • 根据源码可以看到它是一个uint32_t类型,在#include "MetadataKind.def"文件中,可以看到它的枚举定义
    • 它的具体种类如下:
    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

    TargetMetadata

    • 在上面我们知道 TargetHeapMetadata 的继承 TargetMetadata
    TargetHeapMetadata : TargetMetadata
    
    • 接下来查看一下TargetMetadata结构
    struct TargetMetadata {
      using StoredPointer = typename Runtime::StoredPointer;
    
      // 省略初始化方法
    
    private:
      /// The kind. Only valid for non-class metadata; getKind() must be used to get
      /// the kind value.
      StoredPointer Kind;
    public:
      // 省略部分方法
    }
    
    • 可以看到 TargetMetadata 中有一个 Kind 成员变量
    • 接着我们在共有方法中,找到一个ConstTargetMetadataPointer函数
    ConstTargetMetadataPointer<Runtime, TargetTypeContextDescriptor>
      getTypeContextDescriptor() const {
        switch (getKind()) {
        case MetadataKind::Class: {
          const auto cls = static_cast<const TargetClassMetadata<Runtime> *>(this);
          if (!cls->isTypeMetadata())
            return nullptr;
          if (cls->isArtificialSubclass())
            return nullptr;
          return cls->getDescription();
        }
        case MetadataKind::Struct:
        case MetadataKind::Enum:
        case MetadataKind::Optional:
          return static_cast<const TargetValueMetadata<Runtime> *>(this)
              ->Description;
        case MetadataKind::ForeignClass:
          return static_cast<const TargetForeignClassMetadata<Runtime> *>(this)
              ->Description;
        default:
          return nullptr;
        }
      }
    
    • 可以看到当kindClass时,会将this强转为TargetClassMetadata类型。

    TargetClassMetadata

    template <typename Runtime>
    struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
      using StoredPointer = typename Runtime::StoredPointer;
      using StoredSize = typename Runtime::StoredSize;
    
      TargetClassMetadata() = default;
      constexpr TargetClassMetadata(const TargetAnyClassMetadata<Runtime> &base,
                 ClassFlags flags,
                 ClassIVarDestroyer *ivarDestroyer,
                 StoredPointer size, StoredPointer addressPoint,
                 StoredPointer alignMask,
                 StoredPointer classSize, StoredPointer classAddressPoint)
        : TargetAnyClassMetadata<Runtime>(base),
          Flags(flags), InstanceAddressPoint(addressPoint),
          InstanceSize(size), InstanceAlignMask(alignMask),
          Reserved(0), ClassSize(classSize), ClassAddressPoint(classAddressPoint),
          Description(nullptr), IVarDestroyer(ivarDestroyer) {}
    
      // The remaining fields are valid only when isTypeMetadata().
      // The Objective-C runtime knows the offsets to some of these fields.
      // Be careful when accessing them.
    
      /// Swift-specific class flags.
      ClassFlags Flags;
    
      /// The address point of instances of this type.
      uint32_t InstanceAddressPoint;
    
      /// The required size of instances of this type.
      /// 'InstanceAddressPoint' bytes go before the address point;
      /// 'InstanceSize - InstanceAddressPoint' bytes go after it.
      uint32_t InstanceSize;
    
      /// The alignment mask of the address point of instances of this type.
      uint16_t InstanceAlignMask;
    
      /// Reserved for runtime use.
      uint16_t Reserved;
    
      /// The total size of the class object, including prefix and suffix
      /// extents.
      uint32_t ClassSize;
    
      /// The offset of the address point within the class object.
      uint32_t ClassAddressPoint;
    
      // Description is by far the most likely field for a client to try
      // to access directly, so we force access to go through accessors.
    private:
      /// An out-of-line Swift-specific description of the type, or null
      /// if this is an artificial subclass.  We currently provide no
      /// supported mechanism for making a non-artificial subclass
      /// dynamically.
      TargetSignedPointer<Runtime, const TargetClassDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;
    
    public:
      // 省略共有方法
    };
    
    • 通过源码可知,该类继承TargetAnyClassMetadata
    • 有很多成员变量,初始化大小,类大小等一些属性

    TargetAnyClassMetadata

    template <typename Runtime>
    struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
      using StoredPointer = typename Runtime::StoredPointer;
      using StoredSize = typename Runtime::StoredSize;
    
    #if SWIFT_OBJC_INTEROP
      constexpr TargetAnyClassMetadata(TargetAnyClassMetadata<Runtime> *isa,
                                       TargetClassMetadata<Runtime> *superclass)
        : TargetHeapMetadata<Runtime>(isa),
          Superclass(superclass),
          CacheData{nullptr, nullptr},
          Data(SWIFT_CLASS_IS_SWIFT_MASK) {}
    #endif
    
      constexpr TargetAnyClassMetadata(TargetClassMetadata<Runtime> *superclass)
        : TargetHeapMetadata<Runtime>(MetadataKind::Class),
          Superclass(superclass),
          CacheData{nullptr, nullptr},
          Data(SWIFT_CLASS_IS_SWIFT_MASK) {}
    
    #if SWIFT_OBJC_INTEROP
      // Allow setting the metadata kind to a class ISA on class metadata.
      using TargetMetadata<Runtime>::getClassISA;
      using TargetMetadata<Runtime>::setClassISA;
    #endif
    
      // Note that ObjC classes does not have a metadata header.
    
      /// The metadata for the superclass.  This is null for the root class.
      ConstTargetMetadataPointer<Runtime, swift::TargetClassMetadata> Superclass;
    
      // TODO: remove the CacheData and Data fields in non-ObjC-interop builds.
    
      /// The cache data is used for certain dynamic lookups; it is owned
      /// by the runtime and generally needs to interoperate with
      /// Objective-C's use.
      TargetPointer<Runtime, void> CacheData[2];
    
      /// The data pointer is used for out-of-line metadata and is
      /// generally opaque, except that the compiler sets the low bit in
      /// order to indicate that this is a Swift metatype and therefore
      /// that the type metadata header is present.
      StoredSize Data;
      
      static constexpr StoredPointer offsetToData() {
        return offsetof(TargetAnyClassMetadata, Data);
      }
    
      /// Is this object a valid swift type metadata?  That is, can it be
      /// safely downcast to ClassMetadata?
      bool isTypeMetadata() const {
        return (Data & SWIFT_CLASS_IS_SWIFT_MASK);
      }
      /// A different perspective on the same bit
      bool isPureObjC() const {
        return !isTypeMetadata();
      }
    };
    
    • TargetAnyClassMetadata的结构中有SuperclassCacheData[2]Data等属性,和OC中的类结构很类似

    • 继承自TargetHeapMetadata,这也证明类本身也是对象。

    • 综上所属,当metadatakindClass时,有如下继承链:

    [图片上传失败...(image-7f57e5-1670486315653)]

    • Class在内存结构由 TargetClassMetadata属性 + TargetAnyClassMetaData属性 + TargetMetaData属性构成,所以得出的metadata的数据结构体如下:
    struct Metadata {
        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: UnsafeMutableRawPointer
        var iVarDestroyer: UnsafeRawPointer
    }
    

    相关文章

      网友评论

          本文标题:Swift底层原理-类与对象

          本文链接:https://www.haomeiwen.com/subject/nbinfdtx.html