Swift底層探索1 - 類(lèi)和結(jié)構(gòu)體(一)

1绍豁、類(lèi)和結(jié)構(gòu)體

1.1 基礎(chǔ)認(rèn)知

類(lèi)和結(jié)構(gòu)體十分相似忌傻,如:

class/struct Person{
    var age: Int
    var name: String

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
}

相同點(diǎn):

  • 定義存儲(chǔ)值的屬性、方法焊夸、初始化器、以及下標(biāo)以使用下標(biāo)語(yǔ)法提供對(duì)其的訪(fǎng)問(wèn)
  • 使用extension來(lái)擴(kuò)展功能
  • 遵循協(xié)議來(lái)提供功能

不同點(diǎn):

  • 類(lèi)能繼承蓝角,結(jié)構(gòu)體不行
  • 類(lèi)型轉(zhuǎn)換使你能夠在運(yùn)行時(shí)檢查和解釋類(lèi)實(shí)例的類(lèi)型
  • 類(lèi)有析構(gòu)函數(shù)用來(lái)釋放其分配的資源
  • 引用計(jì)數(shù)允許對(duì)一個(gè)類(lèi)實(shí)例有多個(gè)引用

1.2 引用類(lèi)型和值類(lèi)型

引用類(lèi)型:即一個(gè)類(lèi)類(lèi)型的變量并不直接存儲(chǔ)具體的實(shí)例對(duì)象阱穗,是對(duì)當(dāng)前存儲(chǔ)具體實(shí)例內(nèi)存地址的引用

值類(lèi)型:相比較類(lèi)類(lèi)型的變量中存儲(chǔ)的是地址,那么值類(lèi)型存儲(chǔ)的就是具體的實(shí)例

  • 要區(qū)分的第一件事就是:類(lèi)是引用類(lèi)型帅容、結(jié)構(gòu)體是值類(lèi)型颇象,可以借助lldb的pox/8g指令來(lái)查看當(dāng)前的變量的內(nèi)存結(jié)構(gòu)

po:p和po的區(qū)別在于使用po只會(huì)輸出對(duì)應(yīng)的值,而p則會(huì)返回值的類(lèi)型以及命令結(jié)果的引用名并徘。

x/8g:讀取內(nèi)存中的值(8g:8字節(jié)格式的輸出)

我們可以通過(guò)運(yùn)行以下代碼來(lái)查看下內(nèi)存結(jié)構(gòu):

class/struct Person{
    var age: Int
    var name: String

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
}


var person = Person(age: 10, name: "小明")
var person_1 = person

結(jié)果1:person和person_1存放了相同的內(nèi)存地址


類(lèi)變量存放的是內(nèi)存地址

結(jié)果2:person和person_1存放了相同的結(jié)構(gòu)體實(shí)例


結(jié)構(gòu)體變量存放的是具體的實(shí)例
  • 要區(qū)分的第二件事:引用類(lèi)型和值類(lèi)型的存儲(chǔ)位置不同:一般情況下遣钳,值類(lèi)型存儲(chǔ)在棧上,引用類(lèi)型存儲(chǔ)在堆上
    內(nèi)存示例圖

內(nèi)存區(qū)域的一些基本認(rèn)知
1麦乞、內(nèi)存的排列是從高到低
2蕴茴、一般只有從0x00000001到0x000007F之間的內(nèi)存才是我們可以操作的區(qū)域
3、一般我們把操作區(qū)域分為Stack(棧 - 存放的是局部變量和函數(shù)運(yùn)行過(guò)程中的上下文)姐直,Heap(堆 - 存儲(chǔ)所有對(duì)象)倦淀,Global(全局區(qū) + 常量區(qū) + 指令區(qū) - 存儲(chǔ)全局變量 + 常量 + 代碼區(qū))
4、當(dāng)棧和堆拉伸后導(dǎo)致內(nèi)存地址重合声畏,即溢出
5撞叽、內(nèi)存分配椧龀桑空間比堆空間快,因?yàn)槎芽臻g需要查找合適的區(qū)域再拷貝值愿棋,完成實(shí)例對(duì)象分配科展,離開(kāi)作用域后銷(xiāo)毀查找并把內(nèi)存塊插入,而椏酚辏空間每次會(huì)直接分配新的空間才睹,離開(kāi)作用域直接回滾銷(xiāo)毀。簡(jiǎn)單來(lái)說(shuō)就是堆始終需要尋找甘邀,而棧不用

所以鑒于堆在時(shí)間和效率上略差琅攘,應(yīng)將class盡量用struct替換,也就是盡量用值類(lèi)型替換引用類(lèi)型

1.3 class和struct的內(nèi)存分布

已知class是在堆松邪,struct在棧坞琴,那么具體內(nèi)存分布又會(huì)是如何,借助frame variable命令進(jìn)行查看

frame varibale -L xxx 查看變量?jī)?nèi)存地址

  • class:

通過(guò)打印結(jié)果可以知道類(lèi)的內(nèi)存分布是比較復(fù)雜的逗抑,首先8字節(jié)存的是指向類(lèi)型信息置济,第二個(gè)8字節(jié)是引用計(jì)數(shù),然后才是屬性信息锋八,p變量仍在堆上,只是存的棧地址

內(nèi)存存值 說(shuō)明
0x0000000100008230 指向類(lèi)型信息(Metadata)
3 引用計(jì)數(shù)(RefCount)
10 person.age
小明 person.name
0x0000000100617338 person.p
  • struct:
    image.png

通過(guò)打印結(jié)果可知結(jié)構(gòu)體的內(nèi)存分布是比較簡(jiǎn)單的护盈,是直接分布在棧上挟纱,即使結(jié)構(gòu)體內(nèi)部含有引用類(lèi)型,p變量仍然在棧上腐宋,只是存的是person這個(gè)對(duì)象的堆空間地址紊服,也就是說(shuō)

內(nèi)存存值 說(shuō)明
10 person.age
小明 person.name
0x0000000109c04110 person.p

2、類(lèi)的初始化器

class Person {
    var name: String
    var age: Int

    // 指定初始化器 (Designated Initializer) + 可失敗 胸竞?
    init?(name: String, age: Int, minAge: Int) {
        if minAge > age {
            return nil
        }
        self.name = name
        self.age = age
    }

    // 指定初始化器 (Designated Initializer) + 必需
    required init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    // 便捷初始化器 (Convenience Initializer) + 必需 + 可失敗
    convenience required init?(name: String) {
        let defaultAge: Int = 8
        self.init(name: name, age: defaultAge)
    }

    // 便捷初始化器 (Convenience Initializer)
    convenience init(age: Int) {
        let defaultName: String = "小明"
        self.init(name: defaultName, age: age)
    }

}

class People : Person{
    var address : String
    
    override init?(name: String, age: Int, minAge: Int) {
        //指定初始化器必須保證在向上委托給父類(lèi)初始化器之前欺嗤,其所在類(lèi)引入的所有屬性都要初始化完成
        self.address = "北京"
        super.init(name: name, age: age, minAge: minAge)
        //指定初始化器必須先向上委托父類(lèi)初始化器,然后才能為繼承的屬性設(shè)置新值
        self.name = "小紅"
    }
    
    convenience required init?(name: String) {
        //required 必需繼承卫枝,便捷初始化器必須先委托同類(lèi)中的其它初始化器
        self.init(name: name, age: 8, minAge: 1)
    }
    
    required init(name: String, age: Int) {
        self.address = "北京"
        super.init(name: name, age: age)
    }
    
}

  • 當(dāng)前的類(lèi)編譯器默認(rèn)不會(huì)自動(dòng)提供成員初始化器煎饼,但是對(duì)于結(jié)構(gòu)體來(lái)說(shuō)編譯 器會(huì)提供默認(rèn)的初始化方法(前提是我們自己沒(méi)有指定初始化器)!
  • 創(chuàng)建類(lèi)和結(jié)構(gòu)體的實(shí)例時(shí)必須為所有的存儲(chǔ)屬性設(shè)置一個(gè)合適的初始值校赤!

1吆玖、指定初始化器必須保證在向上委托給父類(lèi)初始化器之前,其所在類(lèi)引入的所有屬性都要初始化完成马篮。
2沾乘、指定初始化器必須先向上委托父類(lèi)初始化器,然后才能為繼承的屬性設(shè)置新值浑测。如果不這樣做翅阵,指定初始化器賦予的新值將被父類(lèi)中的初始化器所覆蓋
3、便捷初始化器必須先委托同類(lèi)中的其它初始化器,然后再為任意屬性賦新值(包括同類(lèi)里定義的屬性)掷匠。如果沒(méi)這么做滥崩,便捷構(gòu)初始化器賦予的新值將被自己類(lèi)中其它指定初始化器所覆蓋。
4槐雾、初始化器在第一階段初始化完成之前夭委,不能調(diào)用任何實(shí)例方法、不能讀取任何實(shí)例屬性的值募强,也不能引用 self 作為值株灸。

  • 可失敗初始化器:就意味著當(dāng)前因?yàn)閰?shù)的不合法或者外部條件的不滿(mǎn)足,存在初始化失敗的情況擎值。這種 Swift 中可失敗初始化器寫(xiě) return nil 語(yǔ)句(init?())
  • 必要初始化器:在類(lèi)的初始化器前添加 required 修飾符來(lái)表明所有該類(lèi)的子類(lèi)都必須 實(shí)現(xiàn)該初始化器(requeired init())

3慌烧、類(lèi)的生命周期和數(shù)據(jù)結(jié)構(gòu)

  • OC和Swift均是通過(guò)LLVM進(jìn)行編譯的

OC 通過(guò) clang 編譯器,編譯成 IR鸠儿,然后再生成可執(zhí)行文件 .o(這里也就是我們的機(jī)器碼)
Swift 則是通過(guò) Swift 編譯器編譯成 IR屹蚊,然后在生成可執(zhí)行文件。

  • Swift 對(duì)象內(nèi)存分配:
    __allocating_init -> swift_allocObject -> swift_allocObject -> swift_slowAlloc -> Malloc
  • Swift 對(duì)象的內(nèi)存結(jié)構(gòu) HeapObject (OC objc_object) 进每,有兩個(gè)屬性: 一個(gè)是Metadata 汹粤,一個(gè)是 RefCount ,默認(rèn)占用 16 字節(jié)大小田晚。
  • Swift類(lèi)的數(shù)據(jù)結(jié)構(gòu)
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
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嘱兼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子贤徒,更是在濱河造成了極大的恐慌芹壕,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件接奈,死亡現(xiàn)場(chǎng)離奇詭異踢涌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)序宦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)睁壁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人挨厚,你說(shuō)我怎么就攤上這事堡僻。” “怎么了疫剃?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵钉疫,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我巢价,道長(zhǎng)凭峡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任或颊,我火速辦了婚禮,結(jié)果婚禮上备燃,老公的妹妹穿的比我還像新娘。我一直安慰自己凌唬,他們只是感情好并齐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著客税,像睡著了一般况褪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上更耻,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天测垛,我揣著相機(jī)與錄音,去河邊找鬼秧均。 笑死食侮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的目胡。 我是一名探鬼主播锯七,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼誉己!你這毒婦竟也來(lái)了起胰?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤巫延,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后地消,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體炉峰,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年脉执,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疼阔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡半夷,死狀恐怖婆廊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情巫橄,我是刑警寧澤淘邻,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站湘换,受9級(jí)特大地震影響宾舅,放射性物質(zhì)發(fā)生泄漏统阿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一筹我、第九天 我趴在偏房一處隱蔽的房頂上張望扶平。 院中可真熱鬧,春花似錦蔬蕊、人聲如沸结澄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)麻献。三九已至,卻和暖如春囱修,著一層夾襖步出監(jiān)牢的瞬間赎瑰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工破镰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留餐曼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓鲜漩,卻偏偏與公主長(zhǎng)得像源譬,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子孕似,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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

  • 類(lèi)和結(jié)構(gòu)體是通用的踩娘,靈活的結(jié)構(gòu),成為程序代碼的基礎(chǔ)喉祭。 您可以通過(guò)使用與常量养渴,變量和函數(shù)完全相同的語(yǔ)法來(lái)定義屬性和方...
    Joker_King閱讀 321評(píng)論 0 0
  • 類(lèi)和結(jié)構(gòu)體對(duì)比 Swift 中類(lèi)和結(jié)構(gòu)體有很多共同點(diǎn)。共同處在于: 定義屬性用于存儲(chǔ)值 定義方法用于提供功能 定義...
    小驢拉磨閱讀 133評(píng)論 0 0
  • 1. 結(jié)構(gòu)體和類(lèi)對(duì)比 共同點(diǎn)泛烙,兩者都可以:定義屬性用于存儲(chǔ)值定義方法用于提供功能定義下標(biāo)操作用于通過(guò)下標(biāo)語(yǔ)法訪(fǎng)問(wèn)它...
    DevXue閱讀 167評(píng)論 0 0
  • 較傳統(tǒng)的OC語(yǔ)言理卑,Swift使用了更多的結(jié)構(gòu)體,在 Swift 中,所有的基本類(lèi)型,都是結(jié)構(gòu)體類(lèi)型 整數(shù)(Inte...
    熱干面一元五閱讀 1,605評(píng)論 3 3
  • 最近項(xiàng)目使用的是OC自赔,后頭看之前用Swift開(kāi)發(fā)的一個(gè)項(xiàng)目時(shí)妈嘹,發(fā)現(xiàn)很多細(xì)節(jié)都忘記了????。為了回憶和以后方便查看绍妨,現(xiàn)...
    wg剛閱讀 525評(píng)論 0 0