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

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

_$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結構蛛株,不過會生成roData

  • class如果沒有顯式繼承某個類翰苫,都被隱式繼承SwiftObject止邮。

屬性

  • 所有屬性都會添加到class_ro_t中的ivars結構中,包括private屬性奏窑。
  • 使用@objc修飾的屬性导披,var屬性會添加set/get方法,let屬性只會添加get方法埃唯。

Swift類的 屬性可以通過 objc-runtime進行修改和獲取撩匕。

方法

  • 使用@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的基礎上增加了一些字段并村。

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

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

HeapObject

Swift中,一個class對象實際上就是一個HeapObject結構體指針哩牍。HeapObjectHeapMetadataInlineRefCounts組成棚潦,HeapMetadata是類對象元數據的指針,InlineRefCounts用于管理引用計數膝昆。

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, 
    實例變量
}

創(chuàng)建對象

swift_allocObject
  • swift_allocObject方法用于創(chuàng)建一個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);//創(chuàng)建一個新對象收叶,
  return object;
}
  • 根據對象大小做字節(jié)對齊處理骄呼,之后調用malloc分配內存,之后會初始化實例變量。
  • metadata表示類對象元數據蜓萄。
  • requiredSizerequiredAlignmentMask表示對象大小和字節(jié)對齊方式隅茎。
swift_initStackObject
  • 在某些場景對象創(chuàng)建會被編譯器優(yōu)化為swift_initStackObject方法。swift_initStackObject在棧上創(chuàng)建一個對象嫉沽。沒有引用計數消耗辟犀,也不用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環(huán)境的弱引用跃捣。

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

引用計數相關方法

  • swift_retainobjc的實現類似夺蛇,對引用計數進行+1疚漆,溢出時將一部分引用計數值保存到sideTable中。
  • swift_release對引用計數進行-1刁赦,當引用計數為0時娶聘,調用銷毀對象方法。
  • swift_weak相關的方法用于管理weak弱引用甚脉。

SwiftObject

Swift中丸升,一個class如果沒有顯式繼承其他的類,都被隱式繼承SwiftObject牺氨。SwiftObject實現了NSObject協議的所有方法和一部分NSObject類的方法狡耻。主要是重寫了一部分方法,將方法實現改為Swift相關方法猴凹。

@interface SwiftObject<NSObject> {
 @private
  Class isa;
  InlineRefCounts refCounts;
}
@end

沒有實現 resolveInstanceMethod夷狰,forwardingTargetForSelector等方法,這些方法可以在找不到特定方法時可以進行動態(tài)處理郊霎,應該是不想提供純 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)
  • 使用消息發(fā)送的方式調用方法购对。
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的方式創(chuàng)建。

Swift

Swift類

創(chuàng)建一個純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

創(chuàng)建一個原生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子類

創(chuàng)建一個Swift-NSObject子類對象蝇刀。

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

Swift泛型類

創(chuàng)建一個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方法站粟。

每一個不同的泛型類型都會創(chuàng)建一個新的ClassMetadata黍图,之后保存到緩存中復用。

swift_allocateGenericClassMetadata:

  • 創(chuàng)建一個新的ClassMetadata結構奴烙。
  • 初始化objc_classswift_class_t相關的屬性, 同時設置isaroData助被。

swift_initClassMetadataImpl:

  • 設置Superclass,如果沒有指明父類切诀,會被設置為SwiftObject揩环。
  • 初始化Vtable
  • 設置class_ro_tInstanceStartInstanceSize字段幅虑,遍歷ivars修改每個ivaroffset丰滑。
  • 將該類注冊到objc runtime
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末倒庵,一起剝皮案震驚了整個濱河市褒墨,隨后出現的幾起案子,更是在濱河造成了極大的恐慌擎宝,老刑警劉巖郁妈,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異认臊,居然都是意外死亡圃庭,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門失晴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拘央,你說我怎么就攤上這事涂屁。” “怎么了灰伟?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵拆又,是天一觀的道長儒旬。 經常有香客問我,道長帖族,這世上最難降的妖魔是什么栈源? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮竖般,結果婚禮上甚垦,老公的妹妹穿的比我還像新娘。我一直安慰自己涣雕,他們只是感情好艰亮,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挣郭,像睡著了一般迄埃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兑障,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天侄非,我揣著相機與錄音,去河邊找鬼流译。 笑死彩库,一個胖子當著我的面吹牛,可吹牛的內容都是我干的先蒋。 我是一名探鬼主播骇钦,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼竞漾!你這毒婦竟也來了眯搭?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤业岁,失蹤者是張志新(化名)和其女友劉穎鳞仙,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體笔时,經...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡棍好,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了允耿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片借笙。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖较锡,靈堂內的尸體忽然破棺而出业稼,到底是詐尸還是另有隱情,我是刑警寧澤蚂蕴,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布衬浑,位于F島的核電站,受9級特大地震影響检号,放射性物質發(fā)生泄漏。R本人自食惡果不足惜稽鞭,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望引镊。 院中可真熱鬧朦蕴,春花似錦、人聲如沸祠乃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亮瓷。三九已至琴拧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嘱支,已是汗流浹背蚓胸。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留除师,地道東北人沛膳。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像汛聚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子倚舀,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,097評論 1 32
  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學習記錄文檔叹哭,今天18年5月份再次想寫文章,發(fā)現簡書還為我保存起的...
    Jenaral閱讀 2,752評論 2 9
  • 本文詳細整理了 Cocoa 的 Runtime 系統的知識痕貌,它使得 Objective-C 如虎添翼风罩,具備了靈活的...
    lylaut閱讀 800評論 0 4
  • 文中的實驗代碼我放在了這個項目中。 以下內容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 923評論 0 6
  • 擁一盞清茶的暖舵稠,將生命行至闌珊超升。 我不知道自己行走了多少路程,輾轉過凋零的熱烈哺徊,瀲滟的孤寂室琢,一抬頭,又將告別這一個...
    彗心姑娘閱讀 457評論 1 5