Swift進(jìn)階02:值類(lèi)型&引用類(lèi)型

值類(lèi)型

我們先大概了解下內(nèi)存的五大區(qū)


內(nèi)存五大區(qū).png
  • 棧的地址比堆的地址大
  • 棧區(qū)內(nèi)存由系統(tǒng)管理的連續(xù)空間,地址從 高地址->低地址
  • 堆區(qū)內(nèi)存由程序員管理,地址從 低地址->高地址
  • 堆區(qū)分配不連續(xù),類(lèi)似鏈表
  • 日常開(kāi)發(fā)中的溢出是指堆棧溢出,可以理解為棧區(qū)與堆區(qū)邊界碰撞的情況
  • 全局區(qū)、常量區(qū)都存儲(chǔ)在Mach-O中的__TEXT cString

我們首先看一個(gè)例子

func test(){
    var age = 18
    var age2 = age
    age = 30
    age2 = 45

    print("age=\(age),age2=\(age2)")
 }
test()

從例子中可以得出,age存儲(chǔ)在棧區(qū)

  • 輸出age地址
  • 獲取age的棧區(qū)地址:po withUnsafePointer(to: &age){print($0)}(指針輸出,后面會(huì)講)
  • 查看age內(nèi)存情況:x/8g 0x00007ffeefbff3e0

x/8g格式化輸出,就是存儲(chǔ)的18的值


值類(lèi)型02.png

age賦值給age2后再次輸出,發(fā)現(xiàn)發(fā)地址是連續(xù)的,且從高到低


值類(lèi)型04.png

值類(lèi)型特點(diǎn):

  1. 地址存儲(chǔ)的就是
  2. 傳遞的是值的副本,也就是深拷貝
  3. 傳遞過(guò)程中不共享狀態(tài)

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

結(jié)構(gòu)體就是結(jié)構(gòu)體

struct HZMPerson{
    var age: Int = 18
    //結(jié)構(gòu)體可以不用默認(rèn)值
    var age2: Int
    //避免值類(lèi)型里面包含引用類(lèi)型
//    var test : HZMPerson2 = HZMPerson2()

}
//值類(lèi)型:值
var Q = HZMPerson()
//結(jié)構(gòu)體傳遞的過(guò)程不共享狀態(tài)
var Q2 = Q
Q2.age = 30
 
結(jié)構(gòu)體01.png

打印Q發(fā)現(xiàn),直接就是值,沒(méi)有任何與地址有關(guān)的信息

結(jié)構(gòu)體02.png
  • 獲取地址:po withUnsafePointer(to: &Q){print($0)}
  • 查看內(nèi)存情況:x/8g 0x0000000100008178

總結(jié):

  • 結(jié)構(gòu)體是值類(lèi)型,且結(jié)構(gòu)體的地址就是第一個(gè)成員的內(nèi)存地址
  • 在結(jié)構(gòu)體中,如果不給屬性默認(rèn)值,編譯是不會(huì)報(bào)錯(cuò)的。即在結(jié)構(gòu)體中屬性可以賦值,也可以不賦值
    值類(lèi)型:
  • 在內(nèi)存中直接存儲(chǔ)值
  • 值類(lèi)型的賦值,是一個(gè)值傳遞的過(guò)程,即相當(dāng)于拷貝了一個(gè)副本,存入不同的內(nèi)存空間,兩個(gè)空間彼此間并不共享狀態(tài)
  • 值傳遞其實(shí)就是深拷貝

引用類(lèi)型

引用類(lèi)型:地址,相當(dāng)于在線(xiàn)表格

類(lèi)

小tips:在類(lèi)中,如果屬性沒(méi)有賦值,也不是可選項(xiàng),編譯會(huì)報(bào)錯(cuò)
需要自己實(shí)現(xiàn)init方法

引用類(lèi)型01.png
  • 打印H,從圖中可以看出,H內(nèi)存空間中存放的是地址
引用類(lèi)型02.png
  • 通過(guò)lldb調(diào)試得知,修改了H2,會(huì)導(dǎo)致H改變,主要是因?yàn)镠2、H1地址中都存儲(chǔ)的是 同一個(gè)堆區(qū)地址,如果修改,修改是同一個(gè)堆區(qū)地址,所以修改H2會(huì)導(dǎo)致H1一起修改,即淺拷貝

注意:

1、地址中存儲(chǔ)的是堆區(qū)地址
2、堆區(qū)地址中存儲(chǔ)的是值
3、在編寫(xiě)代碼過(guò)程中,應(yīng)該盡量避免值類(lèi)型包含引用類(lèi)型

mutating&inout

mutating

通過(guò)結(jié)構(gòu)體定義一個(gè),主要有push、pop方法,此時(shí)我們需要?jiǎng)討B(tài)修改棧中的數(shù)組
如果是以下這種寫(xiě)法,會(huì)直接報(bào)錯(cuò),原因是值類(lèi)型本身是不允許修改屬性

mutating01.png

mutating02.png

我們?cè)俅瓮ㄟ^(guò)SIL文件查看,發(fā)現(xiàn)selflet類(lèi)型,當(dāng)我們修改items時(shí)就相當(dāng)于修改self,所以不可修改。

mutating03.png

當(dāng)我們嘗試使用另一種方式來(lái)修改,實(shí)際最終打印的還是空

mutating04.png

當(dāng)我們?yōu)楹瘮?shù)添加一個(gè)mutating修飾的時(shí)候,發(fā)現(xiàn)可以進(jìn)行修改了,這是為什么?我們來(lái)查看下SIL文件

mutating05.png

查看其SIL文件,找到push函數(shù),發(fā)現(xiàn)與之前有所不同,push添加mutaing(只用于值類(lèi)型)后,本質(zhì)上是給值類(lèi)型函數(shù)添加了inout關(guān)鍵字,相當(dāng)于在值傳遞的過(guò)程中,傳遞的是引用(即地址)

inout

inout01.png

一般情況下,在函數(shù)的聲明中,默認(rèn)的參數(shù)都是不可變的

inout02.png

如果想要直接修改,需要給參數(shù)加上inout關(guān)鍵字

總結(jié):
1、結(jié)構(gòu)體中的函數(shù)如果想修改其中的屬性,需要在函數(shù)前加上mutating,而類(lèi)則不用

2、mutating本質(zhì)也是加一個(gè) inout修飾的self

3、Inout相當(dāng)于取地址,可以理解為地址傳遞,即引用

4、mutating修飾方法,而inout 修飾參數(shù)

總結(jié)

通過(guò)上述LLDB查看結(jié)構(gòu)體 & 類(lèi)的內(nèi)存模型,有以下總結(jié):

  • 值類(lèi)型,相當(dāng)于一個(gè)本地excel,當(dāng)我們通過(guò)QQ傳給你一個(gè)excel時(shí),就相當(dāng)于一個(gè)值類(lèi)型,你修改了什么我們這邊是不知道的

  • 引用類(lèi)型,相當(dāng)于一個(gè)在線(xiàn)表格,當(dāng)我們和你共同編輯一個(gè)在線(xiàn)表格時(shí),就相當(dāng)于一個(gè)引用類(lèi)型,兩邊都會(huì)看到修改的內(nèi)容

  • 結(jié)構(gòu)體函數(shù)修改屬性, 需要在函數(shù)前添加mutating關(guān)鍵字,本質(zhì)是給函數(shù)的默認(rèn)參數(shù)self添加了inout關(guān)鍵字,將selflet常量改成了var變量

方法調(diào)度

靜態(tài)派發(fā)

方法調(diào)度01.png

callq 就是一個(gè)指令的跳轉(zhuǎn),就是執(zhí)行我們的函數(shù)方法。值類(lèi)型對(duì)象的函數(shù)的調(diào)用方式是靜態(tài)調(diào)用,即直接地址調(diào)用,調(diào)用函數(shù)指針,這個(gè)函數(shù)指針在編譯、鏈接完成后就已經(jīng)確定了,存放在代碼段,而結(jié)構(gòu)體內(nèi)部并不存放方法。因此可以直接通過(guò)地址直接調(diào)用

方法調(diào)度02.png

打開(kāi)打開(kāi)demo的Mach-O可執(zhí)行文件,其中的__text段,就是所謂的代碼段,需要執(zhí)行的匯編指令都在這里

方法調(diào)度03.png

直接地址調(diào)用后面是符號(hào),這個(gè)符號(hào)哪里來(lái)的?

是從Mach-O文件中的符號(hào)表Symbol Tables,但是符號(hào)表中并不存儲(chǔ)字符串,字符串存儲(chǔ)在String Table(字符串表,存放了所有的變量名和函數(shù)名,以字符串形式存儲(chǔ)),然后根據(jù)符號(hào)表中的偏移值到字符串中查找對(duì)應(yīng)的字符,然后進(jìn)行命名重整:工程名+類(lèi)名+函數(shù)名,如下所示

方法調(diào)度04.png

  • Symbol Table:存儲(chǔ)符號(hào)位于字符串表的位置
  • Dynamic Symbol Table:動(dòng)態(tài)庫(kù)函數(shù)位于符號(hào)表的偏移信息

命名重整規(guī)則先不用考慮,因?yàn)橛忻羁梢灾苯舆€原
查看符號(hào)表:nm mach-o文件路徑

方法調(diào)度05.png

通過(guò)命令還原符號(hào)名稱(chēng):xcrun swift-demangle 符號(hào)

方法調(diào)度06.png

如果將edit scheme -> run中的debug改成release,編譯后查看,在可執(zhí)行文件目錄下,多一個(gè)后綴為dSYM的文件,此時(shí),再去Mach-O文件中查找teach,發(fā)現(xiàn)是找不到,其主要原因是因?yàn)?code>靜態(tài)鏈接的函數(shù),實(shí)際上是不需要符號(hào)的,一旦編譯完成,其地址確定后,當(dāng)前的符號(hào)表就會(huì)刪除當(dāng)前函數(shù)對(duì)應(yīng)的符號(hào),在release環(huán)境下,符號(hào)表中存儲(chǔ)的只是不能確定地址的符號(hào)

函數(shù)符號(hào)命名規(guī)則

#include <stdio.h>
void test(){    }

對(duì)于C函數(shù)來(lái)說(shuō),命名的重整規(guī)則就是在函數(shù)名之前加_(注意:C中不允許函數(shù)重載,因?yàn)闆](méi)有辦法區(qū)分)


命名規(guī)則01.png

對(duì)于OC來(lái)說(shuō),也不支持函數(shù)重載,其符號(hào)命名規(guī)則是-[類(lèi)名 函數(shù)名]


命名規(guī)則02.png

Swift通過(guò)復(fù)雜的命名重整規(guī)則,確保符號(hào)的唯一性,這樣這兩個(gè)方法才不會(huì)報(bào)重命名的錯(cuò)誤,OC與C都不行 C++可以


命名規(guī)則03.png

補(bǔ)充:ASLR

  • 通過(guò)運(yùn)行發(fā)現(xiàn),Mach-O中的地址與調(diào)試時(shí)直接獲取的地址是有一定偏差的,其主要原因是實(shí)際調(diào)用時(shí)地址多了一個(gè)ASLR(地址空間布局隨機(jī)化 address space layout randomizes

  • 可以通過(guò)image list查看,其中0x0000000100000000是程序運(yùn)行的首地址,后8位是隨機(jī)偏移00000000(即ASLR)

  • 匯編地址 = Mach-O中的地址(靜態(tài)基地址) + image list中首地址的后8位(ASLR)

動(dòng)態(tài)派發(fā)

匯編指令補(bǔ)充

  • blr:帶返回的跳轉(zhuǎn)指令,跳轉(zhuǎn)到指令后邊跟隨寄存器中保存的地址
  • mov:將某一寄存器的值復(fù)制到另一寄存器(只能用于寄存器與起存起或者 寄存器與常量之間 傳值,不能用于內(nèi)存地址)
  • mov x1, x0 將寄存器x0的值復(fù)制到寄存器x1中
  • ldr:將內(nèi)存中的值讀取到寄存器中
    ldr x0, [x1, x2] 將寄存器x1和寄存器x2 相加作為地址,取該內(nèi)存地址的值翻入寄存器x0中
  • str:將寄存器中的值寫(xiě)入到內(nèi)存中
    str x0, [x0, x8] 將寄存器x0的值保存到內(nèi)存[x0 + x8]處
  • bl:跳轉(zhuǎn)到某地址
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,310評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,951評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,796評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,566評(píng)論 6 407
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,055評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,303評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,799評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,683評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,899評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,135評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,520評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,757評(píng)論 1 282
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,528評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,844評(píng)論 2 372

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