Swift類結(jié)構(gòu)探究

對(duì)于iOS開發(fā)恢口,OC語言前端使用Clang編譯器咏瑟,swift語言前端使用swift編譯器swiftc饱溢,這兩個(gè)編譯器將我們寫的代碼編譯生成IR中間代碼,后端都是通過LLVM進(jìn)行優(yōu)化窟她,接著交給代碼生成器生成機(jī)器語言,最終形成.o機(jī)器執(zhí)行文件蔼水。
在研究Swift之前震糖,先來探究一下Swift類結(jié)構(gòu)。

一趴腋、Swift類初始化

先寫一段簡(jiǎn)單的代碼:

class Person {
    var age: Int = 33
    var name: String = "chen"
}

let t = LGPerson()

生成SIL(Swift intermediate language)命令:swiftc -emit-sil main.swift | xcrun swift-demangle >> ./main.sil && open main.sil
xcrun swift-demangle用于還原變量吊说,下文未用。

class Person {
  @_hasStorage @_hasInitialValue var age: Int { get set }
  @_hasStorage @_hasInitialValue var name: String { get set }
  @objc deinit
  init()
}

@_hasStorage @_hasInitialValue let t: Person { get }

// t
sil_global hidden [let] @$s4main1tAA6PersonCvp : $Person

// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @$s4main1tAA6PersonCvp             // id: %2
  %3 = global_addr @$s4main1tAA6PersonCvp : $*Person // user: %7
  %4 = metatype $@thick Person.Type               // user: %6
  // function_ref Person.__allocating_init()
  %5 = function_ref @$s4main6PersonCACycfC : $@convention(method) (@thick Person.Type) -> @owned Person // user: %6
  %6 = apply %5(%4) : $@convention(method) (@thick Person.Type) -> @owned Person // user: %7
  store %6 to %3 : $*Person                       // id: %7
  %8 = integer_literal $Builtin.Int32, 0          // user: %9
  %9 = struct $Int32 (%8 : $Builtin.Int32)        // user: %10
  return %9 : $Int32                              // id: %10
} // end sil function 'main'
  1. @main 標(biāo)識(shí)當(dāng)前swift文件的入口函數(shù)优炬,SIL標(biāo)識(shí)符號(hào)名稱以@作為前綴颁井。
  2. %0,%1...在SIL中也叫寄存器,類似代碼中的常量蠢护,一旦賦值后不可修改雅宾。如果SIL中還要繼續(xù)使用,就需要使用新的寄存器葵硕。

SIL代碼解讀:

alloc_global @$s4main1tAA6PersonCvp     :
@$s4main1tAA6PersonCvp反混淆出來就是Person秀又,創(chuàng)建全局變量Person
%3 = global_addr @$s4main1tAA6PersonCvp : $*Person    :讀取全局變量Person地址单寂,賦值給%3
%4 = metatype $@thick Person.Type      :讀取Person的Type,賦值給%4
%5 = function_ref @$s4main6PersonCACycfC       : $@convention(method) (@thick Person.Type) -> @owned Person      : 定義一個(gè)function_ref即函數(shù)吐辙,就是%5宣决,這個(gè)函數(shù)入?yún)⑹荘erson.Type
%6 = apply %5(%4) : $@convention(method) (@thick Person.Type) -> @owned Person   
    :調(diào)用函數(shù)%5,入?yún)⒕褪?4昏苏,將返回結(jié)果賦給%6
store %6 to %3 : $*Person      :將%6的結(jié)果存儲(chǔ)到%3尊沸,%3是LGPerson的地址
%8 = integer_literal $Builtin.Int32, 0 和 %9 = struct $Int32 (%8 : $Builtin.Int32) 
     :就是構(gòu)建一個(gè)Int值
return %9 : $Int32:  最終返回值

總結(jié)一下,main函數(shù)贤惯,通過Type返回了一個(gè)全局變量Person洼专。

實(shí)例化過程__allocating_init

// Person.__allocating_init()
sil hidden [exact_self_class] @$s4main6PersonCACycfC : $@convention(method) (@thick Person.Type) -> @owned Person {
// %0 "$metatype"
bb0(%0 : $@thick Person.Type):
  %1 = alloc_ref $Person                          // user: %3
  // function_ref Person.init()
  %2 = function_ref @$s4main6PersonCACycfc : $@convention(method) (@owned Person) -> @owned Person // user: %3
  %3 = apply %2(%1) : $@convention(method) (@owned Person) -> @owned Person // user: %4
  return %3 : $Person                             // id: %4
} // end sil function '$s4main6PersonCACycfC'

代碼解讀

%1 = alloc_ref $Person   :讀取Person的alloc_ref方法地址,給%1
%2 = function_ref @$s4main6PersonCACycfc : $@convention(method) (@owned Person) -> @owned Person           : 讀取Person.init()函數(shù)地址孵构,給%2
%3 = apply %2(%1) : $@convention(method) (@owned Person) -> @owned Person // user: %4         : 調(diào)用alloc_ref創(chuàng)建一個(gè)Person實(shí)例對(duì)象屁商,給%3
return %3 : $Person       :返回%3的實(shí)例對(duì)象

總結(jié)一下就是,調(diào)用alloc_ref颈墅、init方法蜡镶,返回對(duì)象。
init方法主要用于初始化變量恤筛,和OC中是一致的官还。這個(gè)過程用Swift代碼會(huì)更簡(jiǎn)潔:Person() == [[Perosn alloc] init]

初始化過程毒坛,在底層是怎么實(shí)現(xiàn)的望伦,通過__allocating_init斷點(diǎn),我們可以跟進(jìn)去

swift實(shí)例對(duì)象的創(chuàng)建流程
__allocating_init -> swift_allocObject-> _swift_allocObject_ -> swift_slowAlloc -> malloc_zone_malloc

具體實(shí)現(xiàn)煎殷,參考Swift源碼屯伞,最終對(duì)象會(huì)被轉(zhuǎn)為HeapObject類型。

二豪直、Swift類的結(jié)構(gòu)

2.1 HeapObject結(jié)構(gòu)體

HeapObject.jpg

HeapObject結(jié)構(gòu)體有兩個(gè)成員變量:元數(shù)據(jù)metadata引用計(jì)數(shù)refCounts愕掏,總共16字節(jié)

2.2 metadata的解析

HeapMetedata —> TargetHeapMetadata —> TargetMetadata 顶伞,他們都是struct類型饵撑,TargetMetadata里面只有一個(gè)成員變量kind
通過注釋getKind()找到如下代碼:

  /// Get the metadata kind.
  MetadataKind getKind() const {
    return getEnumeratedMetadataKind(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);
}

getKind是調(diào)用getEnumeratedMetadataKind唆貌,而入?yún)?code>kind是uint64_t類型滑潘,占8字節(jié)大小,所以元數(shù)據(jù)metadata8字節(jié)大小锨咙。
kind類型如下:

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

metadata的數(shù)據(jù)結(jié)構(gòu)體

struct swift_class_t: NSObject{
    void *kind;//相當(dāng)于OC中的isa语卤,kind的實(shí)際類型是unsigned long。兼容oc,swift對(duì)象轉(zhuǎn)換為oc時(shí)粹舵,即為isa指針钮孵。
    void *superClass;
    void *cacheData;
    void *data;
    uint32_t flags; //4字節(jié)
    uint32_t instanceAddressOffset;//4字節(jié)
    uint32_t instanceSize;//4字節(jié)
    uint16_t instanceAlignMask;//2字節(jié)
    uint16_t reserved;//2字節(jié)
    
    uint32_t classSize;//4字節(jié)
    uint32_t classAddressOffset;//4字節(jié)
    void *description;
    ...
}
2.3 refCounts

接著我們看看refCountsrefCountsInlineRefCounts類型眼滤,搜索InlineRefCounts

typedef RefCounts<InlineRefCountBits> InlineRefCounts;

InlineRefCountsRefCounts類型

RefCounts.jpg

RefCountsclass類型巴席,確切的說,refCounts是個(gè)指針诅需,占8字節(jié)大小漾唉。

綜上所述

  • swift類本質(zhì)是HeapObject
  • HeapObject默認(rèn)大小為16字節(jié): metadata(struct)8字節(jié)refCounts(class)8字節(jié)
    所以,上文堰塌,Person的size應(yīng)該為40字節(jié) = age(Int)占8字節(jié) + name(String)占16字節(jié) + HeapObject(16字節(jié))赵刑。

驗(yàn)證一下

class Person {
    var age: Int = 33
    var name: String = "chen"
}

let t = Person()


print("Int32 大小: \(MemoryLayout<Int32>.size)")
print("Int64 大小: \(MemoryLayout<Int64>.size)")
print("Int 大小: \(MemoryLayout<Int>.size)")
print("Srtring 大小: \(MemoryLayout<String>.size)")

print("Person 大小: \(class_getInstanceSize(Person.self))")

打印結(jié)果是:


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市场刑,隨后出現(xiàn)的幾起案子般此,更是在濱河造成了極大的恐慌,老刑警劉巖牵现,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铐懊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡施籍,警方通過查閱死者的電腦和手機(jī)居扒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門概漱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丑慎,“玉大人,你說我怎么就攤上這事瓤摧「土眩” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵照弥,是天一觀的道長(zhǎng)腻异。 經(jīng)常有香客問我,道長(zhǎng)这揣,這世上最難降的妖魔是什么悔常? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮给赞,結(jié)果婚禮上机打,老公的妹妹穿的比我還像新娘。我一直安慰自己片迅,他們只是感情好残邀,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般芥挣。 火紅的嫁衣襯著肌膚如雪驱闷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天空免,我揣著相機(jī)與錄音空另,去河邊找鬼。 笑死鼓蜒,一個(gè)胖子當(dāng)著我的面吹牛痹换,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播都弹,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼娇豫,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了畅厢?” 一聲冷哼從身側(cè)響起鹃愤,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎娶桦,沒想到半個(gè)月后踱蠢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咪辱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年振劳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片油狂。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡历恐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出专筷,到底是詐尸還是另有隱情弱贼,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布磷蛹,位于F島的核電站吮旅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏味咳。R本人自食惡果不足惜庇勃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望槽驶。 院中可真熱鬧责嚷,春花似錦、人聲如沸捺檬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至聂受,卻和暖如春蒿秦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛋济。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工棍鳖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碗旅。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓渡处,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親祟辟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子医瘫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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