(轉(zhuǎn))Objective-C 拾遺:從Heap and Stack到Block

Objective-C 拾遺:從Heap and Stack到Block

Stack和Heap

heap和stack是內(nèi)存管理的兩個重要概念。在這里我們指的不是數(shù)據(jù)結(jié)構(gòu)上面的堆與棧,在這里指的是內(nèi)存的分配區(qū)域脆栋。

stack的空間由操作系統(tǒng)進(jìn)行分配。

在現(xiàn)代操作系統(tǒng)中,一個線程會分配一個stack. 當(dāng)一個函數(shù)被調(diào)用,一個stack frame(棧幀)就會被壓到stack里嘁扼。里面包含這個函數(shù)涉及的參數(shù),局部變量,返回地址等相關(guān)信息宙橱。當(dāng)函數(shù)返回后,這個棧幀就會被銷毀捻悯。而這一切都是自動的,由系統(tǒng)幫我們進(jìn)行分配與銷毀憔杨。對于程序員是透明的,我們不需要手動調(diào)度鸟赫。

heap的空間需要手動分配。

heap與動態(tài)內(nèi)存分配相關(guān),內(nèi)存可以隨時在堆中分配和銷毀芍秆。我們需要明確請求內(nèi)存分配與內(nèi)存銷毀惯疙。 簡單來說,就是malloc與free.

Objective-C中的Stack和Heap

首先所有的Objective-C對象都是分配在heap的妖啥。 在OC最典型的內(nèi)存分配與初始化就是這樣的。

NSObject *obj = [[NSObject alloc] init];

一個對象在alloc的時候对碌,就在Heap分配了內(nèi)存空間荆虱。

stack對象通常有速度的優(yōu)勢,而且不會發(fā)生內(nèi)存泄露問題朽们。那么為什么OC的對象都是分配在heap的呢怀读? 原因在于:

stack對象的生命周期所導(dǎo)致的問題。例如一旦函數(shù)返回骑脱,則所在的stack frame就會被摧毀菜枷。那么此時返回的對象也會一并摧毀。這個時候我們?nèi)etain這個對象是無效的叁丧。因為整個stack frame都已經(jīng)被摧毀了啤誊。簡單而言,就是stack對象的生命周期不適合Objective-C的引用計數(shù)內(nèi)存管理方法拥娄。

stack對象不夠靈活蚊锹,不具備足夠的擴展性。創(chuàng)建時長度已經(jīng)是固定的,而stack對象的擁有者也就是所在的stack frame

關(guān)于Block

問題的由來:

洋洋灑灑講了前面這些東西,其實為什么決定寫這篇總結(jié)呢,簡單講講最初的問題稚瘾。

那就是在之前我在類中聲明block屬性時,一直用的是strong修飾符牡昆。因為我一直把block當(dāng)成一個普通的OC對象來看待。并且也沒有出現(xiàn)過問題摊欠。后來閱讀一些別人的源代碼和博客,發(fā)現(xiàn)不少人都是使用copy修飾符,于是引起了這篇探索丢烘≈拢可以簡單地把這個問題總結(jié)為:為什么block需要使用copy修飾符?

簡單的答案:

首先在官方文檔《Programming with Objective-C》里面寫到,初學(xué)閱讀的時候沒有注意到這個細(xì)節(jié):

You should specify copy as the property attribute, because a block needs to be copied to keep track of its captured state outside of the original scope. This isn’t something you need to worry about when using Automatic Reference Counting, as it will happen automatically, but it’s best practice for the property attribute to show the resultant behavior

在這里官方叫我們使用copy修飾符,雖然在ARC時代已經(jīng)不需要再顯式聲明了,也就是使用strong是沒有問題的,但是仍然建議我們使用copy以顯示相關(guān)拷貝行為播瞳。問題到這里就基本結(jié)束了掸刊。目前使用strong和copy都是沒有問題的。

深入探索:

但是在這里仍然無法解答我的疑惑,需要使用copy修飾符的根本原因是什么狐史。所以繼續(xù)探索痒给。

最終得到的答案是這與block對象在創(chuàng)建時是stack對象有關(guān)。

所以,其實Objective-C是有它的Stack object的骏全。是的,那就是block.

首先我們先對block進(jìn)行進(jìn)一步的認(rèn)識苍柏。

在Objective-C語言中,一共有3種類型的block:

_NSConcreteGlobalBlock 全局的靜態(tài)block姜贡,不會訪問任何外部變量试吁。

_NSConcreteStackBlock 保存在棧中的block,當(dāng)函數(shù)返回時會被銷毀楼咳。

_NSConcreteMallocBlock 保存在堆中的block熄捍,當(dāng)引用計數(shù)為0時會被銷毀。

這里我們主要基于內(nèi)存管理的角度對它們進(jìn)行分類母怜。

NSConcreteGlobalBlock,這種不捕捉外界變量的block是不需要內(nèi)存管理的,這種block不存在于Heap或是Stack而是作為代碼片段存在,類似于C函數(shù)余耽。

NSConcreteStackBlock。這就是這次探索的重點了,需要涉及到外界變量的block在創(chuàng)建的時候是在stack上面分配空間的,也就是一旦所在函數(shù)返回,則會被摧毀苹熏。這就導(dǎo)致內(nèi)存管理的問題,如果我們希望保存這個block或者是返回它,如果沒有做進(jìn)一步的copy處理,則必然會出現(xiàn)問題碟贾。

舉個栗子(來自參考資料block quiz),在手動管理引用計數(shù)時,如果在exampleD_getBlock方法返回block時對block進(jìn)行[[block copy] autorelease]的操作,則方法執(zhí)行完畢后,block就會被銷毀,則返回block是無效的轨域。

typedef void (^dBlock)();

dBlock exampleD_getBlock() {

char d = 'D';

return ^{

printf("%c\n", d);

};

}

void exampleD() {

exampleD_getBlock()();

}

NSConcreteMallocBlock,因此為了解決block作為Stack object的這個問題,我們最終需要把它拷貝到堆上面來袱耽。而此時NSConcreteMallocBlock扮演的就是這個角色干发。

拷貝到堆后,block的生命周期就與一般的OC對象一樣了,我們通過引用計數(shù)來對其進(jìn)行內(nèi)存管理朱巨。

真正的答案:

因此答案便是因為block在創(chuàng)建時是stack對象,如果我們需要在離開當(dāng)前函數(shù)仍能夠使用我們創(chuàng)建的block。我們就需要把它拷貝到堆上以便進(jìn)行以引用計數(shù)為基礎(chǔ)的內(nèi)存管理枉长。

ARC的疑團(tuán):

解答完最初的問題后,新的問題又出現(xiàn)在我的腦海冀续。那就是ARC是如何進(jìn)行block的內(nèi)存管理的呢,對于普通的OC對象之前已經(jīng)在內(nèi)存管理里面進(jìn)行總結(jié)過。

那么block在ARC下是如何從棧管理正確過渡到堆的管理的呢:

我在網(wǎng)上查閱了許多資料與博文,有部分總結(jié)是:

在ARC下NSConcreteStackBlock類型的block會替換成NSConcreteMallocBlock類型

其實這是不夠準(zhǔn)確的,來自蘋果LLVM ARC的文檔中談到:

With the exception of retains done as part of initializing a __strong parameter variable or reading a __weak variable, whenever these semantics call for retaining a value of block-pointer type, it has the effect of a Block_copy. The optimizer may remove such copies when it sees that the result is used only as an argument to a call.

也就是說ARC幫助我們完成了copy的工作,在ARC下,即使你聲明的修飾符是strong,實際上效果是與聲明為copy一樣的搀暑。

因此在ARC情況下,創(chuàng)建的block仍然是NSConcreteStackBlock類型,只不過當(dāng)block被引用或返回時,ARC幫助我們完成了copy和內(nèi)存管理的工作沥阳。

總結(jié)和心得:

其實用一句話總結(jié)便是:

在ARC下,我們可以將block看做一個正常的OC對象,與其他對象的內(nèi)存管理沒什么不同。

有時我們可能簡單地從博客和文檔上面得到一句簡單的結(jié)論就夠了自点。但是如果我們不斷探索桐罕,不斷思考,那么我們的收獲會更大,更深。可能不僅僅是一句知識點,更多的是探索的方法和過程功炮。對一件事情剝繭抽絲,還原本質(zhì)的過程對我來說也是一種享受,一種修行溅潜。

經(jīng)過了一系列探索,最終理解了block的概念,了解了block的實現(xiàn),弄懂了block的內(nèi)存管理。

加油,繼續(xù)修行~

參考資料:

What and where are the stack and heap?

Cocoa blocks as strong pointers vs copy

Should I still copy/Block_copy the blocks under ARC?

Stack and Heap Objects in Objective-C

談Objective-C Block的實現(xiàn)

Objective-C Blocks Quiz

原文出處: 100mango github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末薪伏,一起剝皮案震驚了整個濱河市滚澜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嫁怀,老刑警劉巖设捐,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異塘淑,居然都是意外死亡萝招,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門存捺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來槐沼,“玉大人,你說我怎么就攤上這事捌治「诠常” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵肖油,是天一觀的道長兼吓。 經(jīng)常有香客問我,道長森枪,這世上最難降的妖魔是什么周蹭? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮疲恢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瓷胧。我一直安慰自己显拳,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布搓萧。 她就那樣靜靜地躺著杂数,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瘸洛。 梳的紋絲不亂的頭發(fā)上揍移,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音反肋,去河邊找鬼那伐。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的罕邀。 我是一名探鬼主播畅形,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼诉探!你這毒婦竟也來了日熬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤肾胯,失蹤者是張志新(化名)和其女友劉穎竖席,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敬肚,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡毕荐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了帘皿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片东跪。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鹰溜,靈堂內(nèi)的尸體忽然破棺而出虽填,到底是詐尸還是另有隱情,我是刑警寧澤曹动,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布斋日,位于F島的核電站,受9級特大地震影響墓陈,放射性物質(zhì)發(fā)生泄漏恶守。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一贡必、第九天 我趴在偏房一處隱蔽的房頂上張望兔港。 院中可真熱鬧,春花似錦仔拟、人聲如沸衫樊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽科侈。三九已至,卻和暖如春炒事,著一層夾襖步出監(jiān)牢的瞬間臀栈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工挠乳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留权薯,地道東北人姑躲。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像崭闲,于是被迫代替她去往敵國和親肋联。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,960評論 2 355

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