Swift Runtime - 類和對象

編譯階段

class PureSwiftClass {
    private var private_var_property = 0
    @objc private var objc_private_var_property = 0
    var instance_property = 0
    @objc let objc_instance_let_property = 0
    @objc var objc_instance_var_property = 0

    func instance_method() {}
    @objc func objc_instance_method() {}
    @objc dynamic func objc_dynamic_instance_method() {}
}
複製代碼

下面是編譯階段生成的類信息:swift

_$s10TestObjectSwiftClassCN:
struct __objc_class {
    _OBJC_METACLASS_$__TtC10TestObjectSwiftClass, // metaclass
    _OBJC_CLASS_$_SwiftObject, // superclass
    __objc_empty_cache, // cache
    0x0, // vtable
    __objc_class__TtC10TestObjectSwiftClass_data+1 // data
}

__objc_class__ObjectSwiftClass_data:
struct __objc_data {
    0x80, // flags
    8,// instance start
    48,                                  // instance size
    0x0,
    0x0,                                 // ivar layout
    "ObjectSwiftClass",                     // name
    __objc_class__TtC10TestObjectSwiftClass_methods, // base methods
    0x0,                                 // base protocols
    __objc_class__TtC10Test6ObjectSwiftClass_ivars, // ivars
    0x0,                                 // weak ivar layout
    __objc_class__TtC10TestObjectSwiftClass_properties // base properties
}

// methods
__objc_class__ObjectSwiftClass_methods:
struct __objc_method_list { 
    0x18,                                // flags
    8                                    // method count
}

struct __objc_method {                                 
    "objc_private_instance_var_property",                     // name
    "q16@0:8",                              // signature
    -[_TtC10TestObjectSwiftClass objc_private_instance_var_property] // implementation
}
struct __objc_method {                                 
    "setObjc_private_var_property:",                     // name
}
struct __objc_method {
    "objc_instance_var_property",                     // name
}
struct __objc_method {
    "setObjc_instance_var_property:",                     // name
}
struct __objc_method {                                 
    "objc_instance_let_property",                     // name
}
struct __objc_method {                                 
    "objc_instance_method",                     // name
}
struct __objc_method {                                 
    "objc_dynamic_instance_method",                     // name
}
struct __objc_method {                                
    "init",                               // name
}

// ivars
__objc_class__TtC10TestObjectSwiftClass_ivars:
struct __objc_ivars {                               
    32,                                  // entsize
    5                                    // count
}
struct __objc_ivar {                                   
    "private_var_property",                     // name
}
struct __objc_ivar {                                   
    "objc_private_var_property",           // name
}
struct __objc_ivar {                                   
    "instance_var_property",                     // name
}
struct __objc_ivar {                                   
    "objc_instance_var_property",           // name
}
struct __objc_ivar {                                   
    "objc_instance_let_property",           // name
}
複製代碼

根據上面編譯器生成的數據,能夠獲得一些信息:緩存

class

  • Swift類編譯階段會生成與Objective-C同樣的類元數據,這也是爲何SwiftObjective-C能夠互相調用。

泛型類不會生成類元數據__objc_class結構,不過會生成roDatabash

  • class若是沒有顯式繼承某個類,都被隱式繼承SwiftObject

屬性

  • 全部屬性都會添加到class_ro_t中的ivars結構中,包括private屬性。
  • 使用@objc修飾的屬性,var屬性會添加set/get方法,let屬性只會添加get方法。

Swift類的 屬性能夠經過 objc-runtime進行修改和獲取。ide

方法

  • 使用@objc修飾的方法會添加到ro_class_tmethods結構中。

Swift結構

ClassMetadata

ClassMetadataSwift中全部類元數據格式。優化

struct objc_object {
    Class isa;
}
struct objc_class: objc_object {
    Class superclass;
    cache_t cache;           
    class_data_bits_t bits;
}
struct swift_class_t: objc_class {
    uint32_t flags;//類標示
    uint32_t instanceAddressOffset;
    uint32_t instanceSize;//對象實例大小
    uint16_t instanceAlignMask;//
    uint16_t reserved;// 保留字段
    uint32_t classSize;// 類對象的大小
    uint32_t classAddressOffset;// 
    void *description;//類描述
};
複製代碼

SwiftObjective-C的類元數據是共用的,Swift類元數據只是Objective-C的基礎上增長了一些字段。ui

源代碼中也有一些地方直接使用 reinterpret_cast進行相互轉換。this

Class objcClass = [objcObject class];
ClassMetadata *classAsMetadata = reinterpret_cast<const ClassMetadata *>(objcClass);
複製代碼

HeapObject

Swift中,一個class對象實際上就是一個HeapObject結構體指針。HeapObjectHeapMetadataInlineRefCounts組成,HeapMetadata是類對象元數據的指針,InlineRefCounts用於管理引用計數。spa

struct HeapObject {
  HeapMetadata const *metadata;
  InlineRefCounts refCounts;
};
複製代碼
  • HeapMetadataObjective-C中的isa_t結構同樣,使用ISA_MASK獲取到類對象。
@interface ObjcClass: NSObject {
}

ObjcClass *objcObject = [ObjcClass new];
HeapObject *heapObject = static_cast<HeapObject *>(objcObject);
ObjcClass *objcObject2 =  static_cast<ObjcClass *>(heapObject);

[heapObject retain];
複製代碼

不過由於 Objective-CSwift引用計數管理方式不同,因此轉換之後依然要使用以前的方式進行引用計數管理。設計

Objective-CSwift對象結構:指針

Objc對象結構 {
    isa_t,
    實例變量
}
Swift對象結構 {
    metadata,
    refCounts, 
    實例變量
}
複製代碼

建立對象

swift_allocObject
  • swift_allocObject方法用於建立一個Swift對象。
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) {
    p = malloc(size);
  } 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;
}
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
                                       size_t requiredSize,
                                       size_t 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);//建立一個新對象,
  return object;
}
複製代碼
  • 根據對象大小作字節對齊處理,以後調用malloc分配內存,以後會初始化實例變量。
  • metadata表示類對象元數據。
  • requiredSizerequiredAlignmentMask表示對象大小和字節對齊方式。
swift_initStackObject
  • 在某些場景對象建立會被編譯器優化爲swift_initStackObject方法。swift_initStackObject在棧上建立一個對象。沒有引用計數消耗,也不用malloc內存。
HeapObject *
swift::swift_initStackObject(HeapMetadata const *metadata,
                             HeapObject *object) {
  object->metadata = metadata;
  object->refCounts.initForNotFreeing();
  return object;
}
複製代碼

銷燬對象

swift_deallocClassInstance
  • swift_deallocClassInstance用於銷燬對象,在對象dealloc時調用。
void swift::swift_deallocClassInstance(HeapObject *object,
                                       size_t allocatedSize,
                                       size_t allocatedAlignMask) {
#if SWIFT_OBJC_INTEROP
  objc_destructInstance((id)object);
#endif
  swift_deallocObject(object, allocatedSize, allocatedAlignMask);//
}
複製代碼
  • 調用objc_destructInstance方法釋放關聯對象和弱引用釋放處理。

Objc runtime的對象弱引用,不是Swift環境的弱引用。

  • 調用swift_deallocObject方法調用free回收內存。

引用計數相關方法

  • swift_retainobjc的實現相似,對引用計數進行+1溢出時將一部分引用計數值保存到sideTable中。
  • swift_release對引用計數進行-1,當引用計數爲0時,調用銷燬對象方法。
  • swift_weak相關的方法用於管理weak弱引用。

SwiftObject

Swift中,一個class若是沒有顯式繼承其餘的類,都被隱式繼承SwiftObjectSwiftObject實現了NSObject協議的全部方法和一部分NSObject類的方法。主要是重寫了一部分方法,將方法實現改成Swift相關方法。

@interface SwiftObject<NSObject> {
 @private
  Class isa;
  InlineRefCounts refCounts;
}
@end
複製代碼

沒有實現 resolveInstanceMethodforwardingTargetForSelector等方法,這些方法能夠在找不到特定方法時能夠進行動態處理,應該是不想提供純 Swift類在這塊的能力。

好比retainrelease方法改成了使用swift runtime進行引用計數管理:

- (id)retain {
  auto SELF = reinterpret_cast<HeapObject *>(self);
  swift_retain(SELF);
  return self;
}
- (void)release {
  auto SELF = reinterpret_cast<HeapObject *>(self);
  swift_release(SELF);
}
複製代碼

由於純 Swift類不能直接與Objective-C交互,那麼SwiftObject這樣設計的目的是什麼?

下面是兩種使用場景:

  • 就是將純Swift類做爲id參數傳遞到Objective-C方法中使用。
- (void)test:(id)object {
  [object retain];
  [object performSelector:@selector(objc_instance_method)];
}
複製代碼
let object = NSObject()
test(object)
複製代碼
  • 使用消息發送的方式調用方法。
class SwiftClass {
    @objc dynamic func objc_dynamic_instance_method() {}
}
let object = SwiftClass()
object.objc_dynamic_instance_method()
複製代碼

不過以上場景應該是不多使用的,不清楚還有沒有其它目的。並且這樣設計的話,純Swift類也應該能夠被Objective-C直接使用。

初始化對象

Objective-C

Objective-C使用Swift-NSObject子類

class SwiftClass: NSObject {
}
複製代碼
SwiftClass *object = [[SwiftClass alloc] init];
複製代碼
  • 由於二進制文件中Swift類包含了和Objective-C同樣的類數據信息,因此能夠直接使用Objective-C的方式建立。

Swift

Swift類

建立一個純Swift類對象。

class SwiftClass {
}
SwiftClass()
複製代碼
swift_getInitializedObjCClass
Class swift::swift_getInitializedObjCClass(Class c) {
  [c self];// 爲了確保objc-runtime realize class
  return c;
}
複製代碼
Class objcClass = swift_getInitializedObjCClass(SwiftClass);
HeapObject *object = swift_allocObject(objcClass);
// 釋放
swift_release(object);
複製代碼

原生Objective-C

建立一個原生Objective-C類對象。

@interface ObjectClass
@end
複製代碼
ObjectClass()
複製代碼
Class objcClass = swift_getInitializedObjCClass(ObjectClass);
Metadata *metadata = swift_getObjCClassMetadata(objcClass);
ClassMetadata *classMetadata = swift_getObjCClassFromMetadata(metadata);
ObjectClass *object = [classMetadata allocWithZone] init];
// 釋放
objc_release(object);
複製代碼

swift_getObjCClassMetadataswift_getObjCClassFromMetadata有什麼做用?

Swift-NSObject子類

建立一個Swift-NSObject子類對象。

class SwiftClass: NSObject {
}
SwiftClass()
複製代碼
Class objcClass = swift_getInitializedObjCClass(SwiftClass);
HeapObject *object = objc_allocWithZone(objcClass);
// 釋放
objc_release(object);
複製代碼

Swift泛型類

建立一個Swift泛型類對象。

class GenericClass<T> {
}
GenericClass<Int>()
複製代碼
MetadataResponse response = swift_getGenericMetadata();
ClassMetadata *classMetadata = swift_allocateGenericClassMetadata();
swift_initClassMetadata2(classMetadata);
HeapObject *object = swift_allocObject(objcClass);
複製代碼
  • 根據泛型類型做爲參數,調用swift_getGenericMetadata方法獲取類對象緩存。存在緩存直接返回,沒有緩存,調用swift_allocateGenericClassMetadata方法。

每個不一樣的泛型類型都會建立一個新的ClassMetadata,以後保存到緩存中複用。

swift_allocateGenericClassMetadata:

  • 建立一個新的ClassMetadata結構。
  • 初始化objc_classswift_class_t相關的屬性, 同時設置isaroData

swift_initClassMetadataImpl:

  • 設置Superclass,若是沒有指明父類,會被設置爲SwiftObject
  • 初始化Vtable
  • 設置class_ro_tInstanceStartInstanceSize字段,遍歷ivars修改每一個ivaroffset
  • 將該類註冊到objc runtime
相關文章
相關標籤/搜索