Swift 中的類

Swift 中,類對(duì)象的結(jié)構(gòu)是否和 Objective-C 一樣呢?

SwiftObject

在swift中,如果沒有明確聲明父類的類,則會(huì)隱式地繼承自 SwiftObject (支持與 Objective-C 混編的前提下,因?yàn)?SwiftObject 是一個(gè) Objective-C 類),隱式繼承來源于 swiftABI/TypeLayout.rst 文檔。

關(guān)于 SwiftObject 的定義如下:

#if SWIFT_OBJC_INTEROP
#if __OBJC__

// Source code: "SwiftObject"
// Real class name: mangled "Swift._SwiftObject"
#define SwiftObject _TtCs12_SwiftObject

#if __has_attribute(objc_root_class)
__attribute__((__objc_root_class__))
#endif
SWIFT_RUNTIME_EXPORT @interface SwiftObject<NSObject> {
 @private
  Class isa;
  SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
}

...
  
@end

namespace swift {

id getDescription(OpaqueValue *value, const Metadata *type);

}

#endif
#endif

namespace swift {

/// Get the NSObject metadata.
const Metadata *getNSObjectMetadata();

}

#endif

SwiftObject 的聲明是 SWIFT_OBJC_INTEROPtrue 的情況下才有聲明的,而在蘋果平臺(tái)下,都是支持 swiftObjective-C 進(jìn)行混編的,因此 SWIFT_OBJC_INTEROP1(true),SWIFT_OBJC_INTEROP 宏的定義可在 Config.h 中找到

/// Does the current Swift platform support "unbridged" interoperation
/// with Objective-C?  If so, the implementations of various types must
/// implicitly handle Objective-C pointers.
///
/// Apple platforms support this by default.
#ifndef SWIFT_OBJC_INTEROP
#ifdef __APPLE__
#define SWIFT_OBJC_INTEROP 1
#else
#define SWIFT_OBJC_INTEROP 0
#endif
#endif

SwiftObject 的定義中可以看到

  • 第一個(gè)成員變量和 Objective-C 一樣是 isa 指針,暫時(shí)猜想和 Objective-C 中的 isa 指針功能一樣
  • 第二個(gè)成員變量是一個(gè) SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS 宏,其定義可在 HeapObject.hRefCount.h 中找到,從命名上看是引用計(jì)數(shù)器
// RefCount.h
// This definition is a placeholder for importing into Swift.
// It provides size and alignment but cannot be manipulated safely there.
typedef struct {
  __swift_uintptr_t refCounts SWIFT_ATTRIBUTE_UNAVAILABLE;
} InlineRefCountsPlaceholder;

#if defined(__swift__)

typedef InlineRefCountsPlaceholder InlineRefCounts;

#else

// HeapObject.h
namespace swift {

struct InProcess;

template <typename Target> struct TargetHeapMetadata;
using HeapMetadata = TargetHeapMetadata<InProcess>;
#else
typedef struct HeapMetadata HeapMetadata;
typedef struct HeapObject HeapObject;
#endif

// The members of the HeapObject header that are not shared by a
// standard Objective-C instance
#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;

#ifndef __swift__
  HeapObject() = default;

  // Initialize a HeapObject header as appropriate for a newly-allocated object.
  constexpr HeapObject(HeapMetadata const *newMetadata) 
    : metadata(newMetadata)
    , refCounts(InlineRefCounts::Initialized)
  { }
  
  // Initialize a HeapObject header for an immortal object
  constexpr HeapObject(HeapMetadata const *newMetadata,
                       InlineRefCounts::Immortal_t immortal)
  : metadata(newMetadata)
  , refCounts(InlineRefCounts::Immortal)
  { }

#ifndef NDEBUG
  void dump() const LLVM_ATTRIBUTE_USED;
#endif

#endif // __swift__
};

在下文的所有測(cè)試代碼中,均使用自定義 Person

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func printData() {
        print("name=\(name), age=\(age)")
    }
}

驗(yàn)證父類

下面通過2種方式來驗(yàn)證在 swift 中,沒有明確聲明父類的類,會(huì)隱式繼承 SwiftObject

  • Objective-C runtime
print(class_getSuperclass(Person.self))

打印結(jié)果為:

Optional(_TtCs12_SwiftObject)
  • MachO文件
Person Superclass

_$s11ClassStruct6PersonCN 這個(gè)結(jié)構(gòu)體可以理解為 Person 類的類對(duì)象,其 superclass 指針指向 _TtCs12_SwiftObject

_TtCs12_SwiftObjectSwiftObject 的真實(shí)類名,可在 SwiftObject.h 的文檔中找到相關(guān)的描述:

// Source code: "SwiftObject"
// Real class name: mangled "Swift._SwiftObject"
#define SwiftObject _TtCs12_SwiftObject

HeapObject

swift 中,基本上分配在堆上的對(duì)象都是一個(gè) HeapObject。其定義可在 HeapObject.h 中找到:

// The members of the HeapObject header that are not shared by a
// standard Objective-C instance
#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 是指向類結(jié)構(gòu) HeapMetadata 的指針,refCounts 所包含的信息比較多,使用了掩碼,其中包含了引用計(jì)數(shù)。

因此 Person 的實(shí)例對(duì)象的成員變量是在偏移量為16個(gè)字節(jié)開始,前16個(gè)字節(jié)分別存放 metadata 的指針和 refCounts 的引用計(jì)數(shù),Person 的實(shí)例對(duì)象對(duì)應(yīng)的內(nèi)存結(jié)構(gòu)可以對(duì)應(yīng)如下的結(jié)構(gòu)體:

struct PersonStruct {
    // 指向類對(duì)象的指針
    let metaData: UnsafePointer
    let refCounts: __uint64_t
    var name: String
    let age: Int
}

定義一個(gè)簡(jiǎn)單的 person 實(shí)例對(duì)象,并定義一個(gè)結(jié)構(gòu)體 personStruct 指向 person 內(nèi)存,并修改 personStruct 結(jié)構(gòu)體的 name 屬性和 age 屬性,看 person 對(duì)象的屬性是否有對(duì)應(yīng)的修改

var person = Person(name: "swift", age: 6)
// 將指向person對(duì)象內(nèi)存地址的指針轉(zhuǎn)成指向PersonStruct結(jié)構(gòu)體的指針,實(shí)則還是指向同一個(gè)內(nèi)存地址
var pStructPointer = Unmanaged.passUnretained(person).toOpaque().bindMemory(to: PersonStruct.self, capacity: personSize)
print(pStructPointer)

pStructPointer.pointee.name = "struct"
pStructPointer.pointee.age = 1
person.printData()
// 打印結(jié)果為:name=struct, age=1

這里需要注意的是,不能把指向 person 實(shí)例對(duì)象內(nèi)存地址的指針轉(zhuǎn)成 PersonStruct 結(jié)構(gòu)體,再修改結(jié)構(gòu)體的屬性值。

var pStruct = Unmanaged.passUnretained(person).toOpaque().bindMemory(to: PersonStruct.self, capacity: personSize).pointee

這種寫法是不會(huì)改變 person 對(duì)象的屬性,因?yàn)榻Y(jié)構(gòu)體是值類型,相當(dāng)于把 person 內(nèi)存的值都拷貝了一份

HeapMetadata

HeapMetadata 可理解為 Objective-C 中的類對(duì)象和元類對(duì)象,其對(duì)應(yīng)的結(jié)構(gòu)可在 Metadata.h 中找到

/// In-process native runtime target.
///
/// For interactions in the runtime, this should be the equivalent of working
/// with a plain old pointer type.
struct InProcess {
  static constexpr size_t PointerSize = sizeof(uintptr_t);
  using StoredPointer = uintptr_t;
  using StoredSignedPointer = uintptr_t;
  using StoredSize = size_t;
  using StoredPointerDifference = ptrdiff_t;

  static_assert(sizeof(StoredSize) == sizeof(StoredPointerDifference),
                "target uses differently-sized size_t and ptrdiff_t");
  
  template <typename T>
  using Pointer = T*;

  template <typename T>
  using SignedPointer = T;
  
  ...
};

/// The common structure of all type metadata.
template <typename Runtime>
struct TargetMetadata {
  using StoredPointer = typename Runtime::StoredPointer;

  /// The basic header type.
  typedef TargetTypeMetadataHeader<Runtime> HeaderType;

  ...
    
private:
  /// The kind. Only valid for non-class metadata; getKind() must be used to get
  /// the kind value.
  StoredPointer Kind;
public:
  /// Get the metadata kind.
  MetadataKind getKind() const {
    return getEnumeratedMetadataKind(Kind);
  }
  
  /// Set the metadata kind.
  void setKind(MetadataKind kind) {
    Kind = static_cast<StoredPointer>(kind);
  }

#if SWIFT_OBJC_INTEROP
protected:
  const TargetAnyClassMetadata<Runtime> *getClassISA() const {
    return reinterpret_cast<const TargetAnyClassMetadata<Runtime> *>(Kind);
  }
  void setClassISA(const TargetAnyClassMetadata<Runtime> *isa) {
    Kind = reinterpret_cast<StoredPointer>(isa);
  }
#endif

public:
  /// Is this a class object--the metadata record for a Swift class (which also
  /// serves as the class object), or the class object for an ObjC class (which
  /// is not metadata)?
  bool isClassObject() const {
    return static_cast<MetadataKind>(getKind()) == MetadataKind::Class;
  }
  
  ...
};

/// The common structure of all metadata for heap-allocated types.  A
/// pointer to one of these can be retrieved by loading the 'isa'
/// field of any heap object, whether it was managed by Swift or by
/// Objective-C.  However, when loading from an Objective-C object,
/// this metadata may not have the heap-metadata header, and it may
/// not be the Swift type metadata for the object's dynamic type.
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
};
using HeapMetadata = TargetHeapMetadata<InProcess>;

上面看起來可能還是有點(diǎn)亂,可以看看下面只保留成員變量的結(jié)構(gòu)體

struct TargetMetadata {
  uintptr_t Kind;
}

struct TargetHeapMetadata : TargetMetadata<Runtime> {
  
};

這么一看,結(jié)構(gòu)體中只有一個(gè)成員變量 Kind,其實(shí) Kind 屬性為 MetadataKind 類型,記錄著此 metadata 的實(shí)際類型,其定義在 MetadataValues.h & MetadataKind.def 中能找到:

/// Non-type metadata kinds have this bit set.
const unsigned MetadataKindIsNonType = 0x400;

/// Non-heap metadata kinds have this bit set.
const unsigned MetadataKindIsNonHeap = 0x200;

// The above two flags are negative because the "class" kind has to be zero,
// and class metadata is both type and heap metadata.

/// Runtime-private metadata has this bit set. The compiler must not statically
/// generate metadata objects with these kinds, and external tools should not
/// rely on the stability of these values or the precise binary layout of
/// their associated data structures.
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"
  /// ABSTRACTMETADATAKIND(Name, Start, End)
///   Represents an abstraction categorization of a range of metadata kind
///   values. Name is the identifier of the range and Start, End are the
///   beginning and end of the range.
#ifndef ABSTRACTMETADATAKIND
#define ABSTRACTMETADATAKIND(Name, Start, End)
#endif

/// NOMINALTYPEMETADATAKIND(Name, Value)
///   Represents the native metadata kind for a swift nominal type. Name is the
///   name of the kind and Value is the integral value used to identify the
///   value. Delegates to METADATAKIND if not defined.
#ifndef NOMINALTYPEMETADATAKIND
#define NOMINALTYPEMETADATAKIND(Name, Value) METADATAKIND(Name, Value)
#endif

/// 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)
  
  LastEnumerated = 0x7FF,
};

可能不太好看,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

TargetMetadata 結(jié)構(gòu)體中有個(gè)函數(shù)可以獲取其類型

struct TargetMetadata {
  ...
    
private:
  /// The kind. Only valid for non-class metadata; getKind() must be used to get
  /// the kind value.
  StoredPointer Kind;
public:
  /// Get the metadata kind.
  MetadataKind getKind() const {
    return getEnumeratedMetadataKind(Kind);
  }
  
  /// Set the metadata kind.
  void setKind(MetadataKind kind) {
    Kind = static_cast<StoredPointer>(kind);
  }
  ...
}

/// Try to translate the 'isa' value of a type/heap metadata into a value
/// of the MetadataKind enum.
inline MetadataKind getEnumeratedMetadataKind(uint64_t kind) {
  if (kind > LastEnumeratedMetadataKind)
    return MetadataKind::Class;
  return MetadataKind(kind);
}

如果 kind 大于 0x7FF 的話,都為 MetadataKind::Class 類型。下面可以看看 Person 類的 Kind

print(String(format: "0x%02lx", Unmanaged.passUnretained(person).toOpaque().load(as: __uint64_t.self)))
// 打印結(jié)果:0x1000092d8

也可以通過 LLDB 來獲取其值:

Person Kind

因此 getEnumeratedMetadataKind(uint64_t kind) 返回 MetadataKind::Class

/// Get the nominal type descriptor if this metadata describes a nominal type,
/// or return null if it does not.
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;
    }
}

在函數(shù) getTypeContextDescriptor() 中可以看到,如果 Kind 類型為 MetadataKind::Class 的話,可以轉(zhuǎn)成 TargetClassMetadata 結(jié)構(gòu)體

/// The structure of all class metadata.  This structure is embedded
/// directly within the class's heap metadata structure and therefore
/// cannot be extended without an ABI break.
///
/// Note that the layout of this type is compatible with the layout of
/// an Objective-C class.
template <typename Runtime>
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
  using StoredPointer = typename Runtime::StoredPointer;
  using StoredSize = typename Runtime::StoredSize;

  // 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:
  /// A function for destroying instance variables, used to clean up after an
  /// early return from a constructor. If null, no clean up will be performed
  /// and all ivars must be trivial.
  TargetSignedPointer<Runtime, ClassIVarDestroyer * __ptrauth_swift_heap_object_destructor> IVarDestroyer;
  
  ...
};
using ClassMetadata = TargetClassMetadata<InProcess>;

/// The portion of a class metadata object that is compatible with
/// all classes, even non-Swift ones.
template <typename Runtime>
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
  using StoredPointer = typename Runtime::StoredPointer;
  using StoredSize = typename Runtime::StoredSize;

#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;
};
using AnyClassMetadata =
  TargetAnyClassMetadata<InProcess>;

接下來通過驗(yàn)證一下,可以定義如下 C++ 結(jié)構(gòu)體:

struct TargetAnyClassMetadata {
    struct TargetAnyClassMetadata* Kind;
    void* superClass;
    void* CacheData[2];
    void* Data;
};

struct TargetClassMetadata {
    uintptr_t Kind;  
    struct AnyClassHeapMetadata* superClass;
    void* CacheData[2];
    void* Data;
  
    uint32_t Flags;
    uint32_t InstanceAddressPoint;
    uint32_t InstanceSize;
    uint16_t InstanceAlignMask;
    uint16_t Reserved;
    uint32_t ClassSize;
    uint32_t ClassAddressPoint;
    uintptr_t Description;
    uintptr_t IVarDestroyer;
};

打印 TargetClassMetadata 中的 InstanceSize 的值,即為 Person 類創(chuàng)建的實(shí)例對(duì)象所占的內(nèi)存大小

let personMetaData = pStructPointer.pointee.metaData.bindMemory(to: TargetClassMetadata.self, capacity: MemoryLayout<TargetClassMetadata>.stride).pointee
print(personMetaData.InstanceSize)
// 打印結(jié)果為:40

Person 中,name 屬性占用16字節(jié),age 屬性占用8字節(jié),隱式繼承的 SwiftObject 的成員變量 metadata 指針和 refCounts 分別都占用 8字節(jié),加起來總共占用40個(gè)字節(jié)用于存放成員變量,但由于內(nèi)存對(duì)齊,實(shí)際分配的內(nèi)存為48字節(jié)

print("person對(duì)象占用的內(nèi)存大小: \(malloc_size(pPointer))")
// 打印結(jié)果為:person對(duì)象占用的內(nèi)存大小: 48

我們可以繼續(xù)獲取其 Kind 值所指向的“元類對(duì)象”,可理解為 Objective-C 中的元類對(duì)象。下面可以打印 Person 的類對(duì)象和元類對(duì)象的地址:

/// 類對(duì)象地址指針
let clsAddress = pStructPointer.pointee.metaData
/// Person元類對(duì)象地址
let metaAddress = String(format: "0x%02lx", personMetaData.Kind)
print("Person類對(duì)象地址: \(clsAddress)")
print("Person元類對(duì)象地址: \(metaAddress)")

打印結(jié)果為:

Person類對(duì)象地址: 0x10000a2d8
Person元類對(duì)象地址: 0x10000a2a0

使用 Hopper Disassembler 查看MachO 文件

MachO

??:不能通過 Objective-C 的運(yùn)行時(shí)來獲取 Swift 類的類對(duì)象和元類對(duì)象,通過 objc_getClass()objc_getMetaClass() 這些 Objective-C 運(yùn)行時(shí)runtime 方法獲取到的類對(duì)象和元類對(duì)象是動(dòng)態(tài)創(chuàng)建出來的,不可取

RefCount 引用計(jì)數(shù)

實(shí)例對(duì)象的第二個(gè)成員變量 refCounts 從名字上看是存放引用計(jì)數(shù),但并不是直接存放的,而且采用了掩碼進(jìn)行存放,類似于 Objective-C 中的 isa 指針,其定義可在 RefCount.h 中找到

// 64-bit inline
// 64-bit out of line
// 32-bit out of line
template <>
struct RefCountBitOffsets<8> {
  /*
   The bottom 32 bits (on 64 bit architectures, fewer on 32 bit) of the refcount
   field are effectively a union of two different configurations:
   
   ---Normal case---
   Bit 0: Does this object need to call out to the ObjC runtime for deallocation
   Bits 1-31: Unowned refcount
   
   ---Immortal case---
   All bits set, the object does not deallocate or have a refcount
   */
  static const size_t PureSwiftDeallocShift = 0;
  static const size_t PureSwiftDeallocBitCount = 1;
  static const uint64_t PureSwiftDeallocMask = maskForField(PureSwiftDealloc);

  static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc);
  static const size_t UnownedRefCountBitCount = 31;
  static const uint64_t UnownedRefCountMask = maskForField(UnownedRefCount);

  static const size_t IsImmortalShift = 0; // overlaps PureSwiftDealloc and UnownedRefCount
  static const size_t IsImmortalBitCount = 32;
  static const uint64_t IsImmortalMask = maskForField(IsImmortal);

  static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount);
  static const size_t IsDeinitingBitCount = 1;
  static const uint64_t IsDeinitingMask = maskForField(IsDeiniting);

  static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting);
  static const size_t StrongExtraRefCountBitCount = 30;
  static const uint64_t StrongExtraRefCountMask = maskForField(StrongExtraRefCount);
  
  static const size_t UseSlowRCShift = shiftAfterField(StrongExtraRefCount);
  static const size_t UseSlowRCBitCount = 1;
  static const uint64_t UseSlowRCMask = maskForField(UseSlowRC);

  static const size_t SideTableShift = 0;
  static const size_t SideTableBitCount = 62;
  static const uint64_t SideTableMask = maskForField(SideTable);
  static const size_t SideTableUnusedLowBits = 3;

  static const size_t SideTableMarkShift = SideTableBitCount;
  static const size_t SideTableMarkBitCount = 1;
  static const uint64_t SideTableMarkMask = maskForField(SideTableMark);
};

typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;

template <typename RefCountBits>
class RefCounts {
  std::atomic<RefCountBits> refCounts;

    ...
};

typedef RefCounts<InlineRefCountBits> InlineRefCounts;
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts;

掩碼對(duì)應(yīng)的值如下表所示:

掩碼名稱 掩碼起始位置 位數(shù)
PureSwiftDeallocShift 0x1UL<<0 1
UnownedRefCountShift 0x1UL<<1 31
IsImmortalShift 0x1UL<<0 32
IsDeinitingShift 0x1UL<<32 1
StrongExtraRefCountShift 0x1UL<<33 30
UseSlowRCShift 0x1UL<<63 1
SideTableShift 0x1UL<<0 62
SideTableMarkShift 0x1UL<<62 1
RefCountBits

通過代碼驗(yàn)證一下 UnownedRefCountShiftStrongExtraRefCountShift

let p1 = Person(name: "swift", age: 6)
let p2 = p1
let p3 = p1
unowned let p4 = p1
unowned let p5 = p1

上面有3個(gè)強(qiáng)引用,2個(gè)無主引用(總數(shù)為2+1,初始值為1),接下來獲取 p1 對(duì)象的 refCounts 值:

person RefCounts

0x0000000600000006 中對(duì)應(yīng)強(qiáng)引用計(jì)數(shù) StrongExtraRefCountShift 的值為 0x3,對(duì)應(yīng)無主引用計(jì)數(shù) UnownedRefCountShift 的值為0x3

若對(duì)象存在弱引用,其弱引用計(jì)數(shù)在 sidetable 中的

weak var p6 = p1
Weak RefCounts

其值已發(fā)生改變,SideTableMarkShift 標(biāo)志位為 1。

Person 內(nèi)存圖解

我自己畫了一幅圖來描述 Person 的內(nèi)存結(jié)構(gòu):

Memory Layout

??:Person 元類對(duì)象不是TargetClassMetadata類型的,Person 類對(duì)象才是

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容