OC基礎(chǔ)知識(shí)梳理 - Stack 和 Heap

一、C 程序的內(nèi)存結(jié)構(gòu)

因?yàn)?Objetive-C 是基于 C 之上的,為了能充分了解 OC 中的 Stack 和 Heap, 讓我們先看看在一個(gè) C 程序中,內(nèi)存是如何分布的。

內(nèi)存結(jié)構(gòu) - 圖片來源:

如圖所示筑凫,一個(gè) C 程序的內(nèi)存分布共有5個(gè)區(qū):

  1. code/代碼區(qū): 代碼區(qū)包含程序編譯后的所有方法,包括系統(tǒng)的方法并村。通常以機(jī)器碼的形式儲(chǔ)存巍实。代碼區(qū)通常是只讀的,并且內(nèi)存大小是固定的橘霎。代碼區(qū)處于內(nèi)存的地位蔫浆,以防止 Stack 與 Heap 覆蓋了代碼區(qū)。

  2. initialized data/全局初始化數(shù)據(jù)區(qū)/靜態(tài)數(shù)據(jù)區(qū): 全局初始化數(shù)據(jù)區(qū)可簡(jiǎn)稱為數(shù)據(jù)區(qū)姐叁,數(shù)據(jù)區(qū)只初始化一次瓦盛,用來保存全局與靜態(tài)變量。進(jìn)一步劃分可劃分為 只讀數(shù)據(jù)區(qū) 與 讀寫數(shù)據(jù)區(qū)外潜,例如原环,當(dāng)創(chuàng)建 char s[] = "Hello"; 時(shí),字符串的指針 s 會(huì)儲(chǔ)存于讀寫數(shù)據(jù)區(qū)处窥,而 "Hello" 字符串會(huì)儲(chǔ)存于只讀數(shù)據(jù)區(qū)嘱吗。

  3. uninitialized data(bss)/未初始化數(shù)據(jù)區(qū): 未初始化數(shù)據(jù)區(qū)用來儲(chǔ)存沒有賦值的全局與靜態(tài)變量。例如滔驾,global int j; 與 static int i; 都會(huì)儲(chǔ)存于這個(gè)區(qū)谒麦。

  4. stack/棧: 棧是一片動(dòng)態(tài)的內(nèi)存區(qū)域,從內(nèi)存的高位往地位擴(kuò)張哆致,一般與堆的擴(kuò)張方向相反绕德。類似于棧的數(shù)據(jù)結(jié)構(gòu),具有LIFO的?特性摊阀。每當(dāng)一個(gè)方法被調(diào)用時(shí)耻蛇,一個(gè) stack frame/棧幀 就會(huì)被壓入棧中,棧幀 里有所調(diào)用方法的返回地址胞此、參數(shù)與局部變量等相關(guān)信息臣咖。當(dāng)當(dāng)前方法返回時(shí),相應(yīng)的棧幀會(huì)被銷毀漱牵。棧內(nèi)的內(nèi)存管理都是由系統(tǒng)進(jìn)行調(diào)用和銷毀的夺蛇,所以程序員不需要進(jìn)行操作。

  5. heap/堆: 一般處于內(nèi)存的低位往高位擴(kuò)張(與的擴(kuò)張方向相反)酣胀。堆是動(dòng)態(tài)內(nèi)存區(qū)蚊惯,內(nèi)存由程序員進(jìn)行分配管理愿卸。開發(fā)時(shí)可使用 malloc(), calloc(), realloc() 進(jìn)行內(nèi)存的分配,并使用 free() 進(jìn)行內(nèi)存的銷毀截型。

Stack 和 Heap 的主要區(qū)別

  1. 訪問速度:stack 的訪問速度比 heap 快
  2. 內(nèi)存限制:stack 一般有內(nèi)存大小限制,而heap沒有儒溉,除非是硬件的性能限制
  3. 作用域:stack 中的變量只會(huì)在方法調(diào)用時(shí)存在宦焦,當(dāng)方法返回時(shí),變量會(huì)被銷毀顿涣。而 heap 中的變量是全局的波闹,能一直訪問,直至該內(nèi)存塊被程序員手動(dòng)銷毀涛碑。
  4. 內(nèi)存碎片化:Stack 上的內(nèi)存由 CPU 管理精堕,不會(huì)出現(xiàn)內(nèi)存碎片化的情況。而處于 Heap 上的內(nèi)存不能保證能充分利用蒲障,可能會(huì)出現(xiàn)內(nèi)存碎片化

何時(shí)使用 Stack 和 Heap

  1. 本地變量首選使用 Stack, 因?yàn)樽x寫更快歹篓,而且內(nèi)存由系統(tǒng)自動(dòng)管理。
  2. 當(dāng)需要使用占用內(nèi)存大的數(shù)據(jù)結(jié)構(gòu)揉阎,使用 Heap庄撮。如 array, set 等, 因?yàn)?Stack 的內(nèi)存大小有限制毙籽。當(dāng)使用的內(nèi)存超出 Stack 可使用范圍內(nèi)洞斯,便會(huì)出現(xiàn) StackOverflow.
  3. 當(dāng)需要一個(gè)作用域大于當(dāng)前方法作用域的變量時(shí),使用 Heap坑赡。例如烙如,一個(gè)方法需要返回一個(gè)數(shù)組。該數(shù)組必須創(chuàng)建于 Heap毅否,因?yàn)?Stack 內(nèi)的局部變量會(huì)在返回時(shí)被銷毀亚铁,返回 Stack 上的變量會(huì)在編譯時(shí)報(bào)錯(cuò)。
  4. 當(dāng)一個(gè)變量的內(nèi)存大小不確定時(shí)搀突,使用 Heap刀闷。如 動(dòng)態(tài)數(shù)組。同理仰迁,因?yàn)?stack 上的內(nèi)存限制甸昏,當(dāng)變量的內(nèi)存大小不確定時(shí),最好選用 Heap徐许。

二施蜜、Objective-C 中的 Stack 與 Heap

在 Objective-C 中,NSObject 包含了一個(gè)類指針 Class *isa, 里面指向?qū)ο箢惖膶傩粤斜泶朴纭⒎椒斜淼刃畔⒎靡嬗?Objective-C 強(qiáng)大的 runtime缸沃,運(yùn)行時(shí)可以再對(duì)類添加方法、添加屬性修械。

換句話說趾牧,NSObject 類在編譯時(shí)是無(wú)法確定其占用內(nèi)存大小的,因?yàn)樵谶\(yùn)行時(shí)可以再對(duì)類進(jìn)行修改肯污,所以理所當(dāng)然的翘单,所有繼承于 NSObject 的類,在新建對(duì)象時(shí)都是分配在 Heap 上的蹦渣。而在 Objective-C 中哄芜,所有的類都是繼承于 NSObject,所以我們新建的對(duì)象都處于 Heap 上柬唯,必須使用引用計(jì)數(shù)進(jìn)行內(nèi)存管理认臊。

Block 的內(nèi)存管理

Objetive-C 中 Block 的內(nèi)存管理與其他的類稍有不同,以下是三個(gè)不同類型的 Block 以及他們的使用場(chǎng)景锄奢,根據(jù)不同的使用情況失晴,程序編譯時(shí)會(huì)創(chuàng)建不一樣類型的 Block:(這里討論的都是使用 ARC 時(shí)的情況,使用 MRC 的話會(huì)稍有不同)

  • NSConcreateGlobalBlock: 內(nèi)存分配在靜態(tài)數(shù)據(jù)區(qū)斟薇,當(dāng) Block 不需要訪問訪問外部變量時(shí)师坎。
id block = ^(){};
NSLog(@"This is a global block: %@", block);
// This is a global block: <__NSGlobalBlock__: 0x1054480a0>
  • NSConcreateStackBlock: 內(nèi)存分配在 Stack,當(dāng) Block 引用外部變量堪滨,而且沒有 owner 時(shí)胯陋,即沒有指針引用當(dāng)前 Block。
int b = 100;
NSLog(@"This is a stack block: %@", ^(){
        int a = b;
    });
// This is a stack block: : <__NSStackBlock__: 0x7ffeea7b68d8>
  • NSConcreateMallocBlock: 內(nèi)存分配在 Heap袱箱,當(dāng) Block 引用外部變量遏乔,并被引用或作為返回值時(shí)
int b = 100;
id block = ^(){
        int a = b;
    };
NSLog(@"This is a Malloc Block being referenced: %@", block);
// This is a Malloc Block being referenced: <__NSMallocBlock__: 0x600003a193e0>
- (void(^)(void))block
{
    int b = 100;
    return ^(){
        int a = b;
    };
}
NSLog(@"This is a Malloc Block being returned: %@", [self block]);
// This is a Malloc Block being returned: <__NSMallocBlock__: 0x600003a19140>

參考資料:

Memory Layout of C Programs
C語(yǔ)言內(nèi)存分配-通俗理解
What's the difference between a stack and a heap?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市发笔,隨后出現(xiàn)的幾起案子盟萨,更是在濱河造成了極大的恐慌,老刑警劉巖了讨,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捻激,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡前计,警方通過查閱死者的電腦和手機(jī)胞谭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來男杈,“玉大人丈屹,你說我怎么就攤上這事×姘簦” “怎么了旺垒?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵彩库,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我先蒋,道長(zhǎng)骇钦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任竞漾,我火速辦了婚禮司忱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘畴蹭。我一直安慰自己,他們只是感情好鳍烁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布叨襟。 她就那樣靜靜地躺著,像睡著了一般幔荒。 火紅的嫁衣襯著肌膚如雪糊闽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天爹梁,我揣著相機(jī)與錄音右犹,去河邊找鬼。 笑死姚垃,一個(gè)胖子當(dāng)著我的面吹牛念链,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播积糯,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼掂墓,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了看成?” 一聲冷哼從身側(cè)響起君编,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎川慌,沒想到半個(gè)月后吃嘿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梦重,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年兑燥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忍饰。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贪嫂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出艾蓝,到底是詐尸還是另有隱情力崇,我是刑警寧澤斗塘,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站亮靴,受9級(jí)特大地震影響馍盟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茧吊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一贞岭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧搓侄,春花似錦瞄桨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至乳讥,卻和暖如春柱查,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背云石。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工唉工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汹忠。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓淋硝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親错维。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奖地,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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