深入理解ObjetiveC的Block

NSArray* getBlockArray()
{
    int num = 100;
    ;
    return [[NSArray alloc]initWithObjects:
    ^ { NSLog(@"this is block0 : %d", num);},
    ^ { NSLog(@"this is block1 : %d", num);},
    ^ { NSLog(@"this is block2 : %d", num);},
     nil];
}

int main(int argc, const char * argv[]) 
{
    NSArray *array = getBlockArray();
    void (^blockObject)(void);
    blockObject = [array objectAtIndex:2];
    blockObject();
}

程序會報(bào)錯(cuò)--EXC_BAD_ACCESS

通常這可以理解為一個(gè)“野指針”錯(cuò)誤,訪問了內(nèi)存中不該訪問的內(nèi)容。

問題在哪?從“野指針”錯(cuò)誤,我們很直接能想到的就是block對象引用到的地址內(nèi)容已經(jīng)不是我們想要的了,簡單說就是block無效了。可block是對象類型的啊,為什么放在數(shù)組對象中回傳失效了呢,加入NSArray的對象本身就應(yīng)該retain過啊。

1. Block與對象
首先我們先反思幾個(gè)問題:
block到底是不是對象?
如果是對象,和某個(gè)已定義的類的實(shí)例對象在使用上是不是一樣的?
如果不一樣,主要的區(qū)別是什么?

對于第一個(gè)問題,蘋果的ObjectiveC官方文檔中在“Working with Blocks”明確說明:
Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary.
可見, Block是Objective C語言中的對象
蘋果在block的文檔中也提過這么一句:
As an optimization, block storage starts out on the stack—just like blocks themselves do.
Clang的文檔中也有說明:
“ *The initial allocation is done on the stack, *but the runtime provides a Block_copy
function **” (Block_copy在下面我會說)
憑這一點(diǎn),我們就可以回答剩下的兩個(gè)問題。

Block對象與一般的類實(shí)例對象有所不同,一個(gè)主要的區(qū)別就是分配的位置不同,block默認(rèn)在棧上分配,一般類的實(shí)例對象在堆上分配。

而這正是導(dǎo)致本文最初提到的那個(gè)問題發(fā)生的根本原因。Block對象在棧上分配,block的引用指向棧幀內(nèi)存,而當(dāng)方法調(diào)用過后,指針指向的內(nèi)存上寫的是什么數(shù)據(jù)就不確定了。但是到此,retain的疑問還是沒有解開。

我們想一想Objective C引用計(jì)數(shù)的原理,retain是對一個(gè)在堆中分配內(nèi)存的對象的引用計(jì)數(shù)做了增加,執(zhí)行release操作的時(shí)候檢查計(jì)數(shù)是否為1,如果是則釋放堆中內(nèi)存。而對于在棧上分配的block對象,這一點(diǎn)顯然有所不同,如果方法調(diào)用返回,棧幀上的數(shù)據(jù)自然會作廢處理,不像堆上內(nèi)存,需要單獨(dú)release,就算NSArray對block對象本身做了retain也無濟(jì)于事。

其實(shí)在Clang的文檔中,**只定義了兩個(gè)Block類型: _NSConcreteGlobalBlock 和 _NSConcreteStackBlock **。而在Console中的Log我們看到的3個(gè)類型應(yīng)該是處理過的顯示,這些字樣在蘋果的文檔和Clang/LLVM的文檔中實(shí)難找到。通過字面上來看,可以認(rèn)為 _NSConcreteGlobalBlock對應(yīng)于 NSGlobalBlock ,_NSConcreteStackBlock對應(yīng)于 NSStackBlock ,而NSMallocBlock則是另一種情況。(實(shí)際上也正是如此)

NSGlobalBlock,我們只要實(shí)現(xiàn)一個(gè)沒有對周圍變量沒有引用的Block,就會顯示為是它。而如果其中加入了對定義環(huán)境變量的引用,就是NSStackBlock。那么NSMallocBlock又是哪來的呢?malloc一詞其實(shí)大家都熟悉,就是在堆上分配動態(tài)內(nèi)存時(shí)。沒錯(cuò),如果你對一個(gè)NSStackBlock對象使用了Block_copy()或者發(fā)送了copy消息,就會得到NSMallocBlock。這一段中的幾項(xiàng)結(jié)論可從代碼實(shí)驗(yàn)得出。

因此,也就得到了下面對block的使用注意點(diǎn)。
對于Global的Block,我們無需多處理,不需retain和copy,因?yàn)榧词鼓氵@樣做了,似乎也不會有什么兩樣。對于Stack的Block,如果不做任何操作,就會向上面所說,隨棧幀自生自滅。而如果想讓它獲得比stack frame更久,那就調(diào)用Block_copy(),讓它搬家到堆內(nèi)存上。而對于已經(jīng)在堆上的block,也不要指望通過copy進(jìn)行“真正的copy”,因?yàn)槠湟玫降淖兞咳匀粫峭环荩谶@個(gè)意義上看,這里的copy和retain的作用已經(jīng)非常類似。
“The runtime provides a Block_copy
function which, given a block pointer, either copies the underlying block object to the heap, setting its reference count to 1 and returning the new block pointer, or (if the block object is already on the heap) increases its reference count by 1. The paired function is Block_release
, which decreases the reference count by 1 and destroys the object if the count reaches zero and is on the heap. *”
在類中,如果有block對象作為property,可以聲明為copy。

轉(zhuǎn)載地址: http://www.molotang.com/articles/1691.html

ARC 下內(nèi)存泄露的那些點(diǎn)
https://www.zybuluo.com/MicroCai/note/67734

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,312評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,993評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,410評論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,778評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,955評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,521評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,266評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,468評論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,696評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,193評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,431評論 2 378

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

  • 開題之前,說下MRC下的內(nèi)存管理機(jī)制。引用計(jì)數(shù)式內(nèi)存管理的思考方式是:1.自己生成的對象,自己持有。2.非自己生成...
    城市之光閱讀 649評論 0 0
  • 前言 Blocks是C語言的擴(kuò)充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,781評論 0 23
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,197評論 30 471
  • 前言: 在ARC(自動引用技術(shù))前,Objective-c都是手動來分配釋放 釋放 計(jì)數(shù)內(nèi)存,其過程非常復(fù)雜。 A...
    IIronMan閱讀 7,103評論 2 39
  • 對Block的內(nèi)存使用相關(guān)的內(nèi)容簡要整理,解釋其中的道理和使用Block需要注意的問題。 1. Block與對...
    Sev3nUp閱讀 732評論 2 4