2013-08-20 22 views
0
MyBlock getBlocks() 
{  
    MyBlock myBlock = ^{ 
     NSLog(@"Hello World!"); 
    }; 

    return myBlock; 
} 

int main(int argc, const char * argv[]) 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    MyBlock myBlock = getBlocks(); 

    myBlock(); 

    [pool drain]; 

    return 0; 
} 

爲什麼此代碼段工作? myBlock應該被銷燬。Objective-C中塊的生命週期

順便說一句,這個片段也可以工作:

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

NSLog(@"%ld", [obj retainCount]); 

MyBlock myBlock = ^{ 
    NSLog(@"Hello World!"); 
    NSLog(@"%ld", [obj retainCount]); 
}; 

[obj release]; 

但[OBJ retainCount]在版畫1而不是2,爲什麼呢?

回答

4

出於性能方面的原因,塊可以分配到堆棧上,並且在複製時只能遷移到堆上。如果你的塊沒有捕獲任何值,編譯器也可以把它變成一個全局塊,其內存存在於某個固定的靜態存儲單元中。

由於你的getBlocks塊沒有捕獲任何東西,它是一個全局塊,它的內存不能被丟棄。它沒有引用計數語義,保留/釋放不會執行任何操作。它的引用永遠是有效的,你總是可以調用它。

如果getBlocks塊做捕捉一些值,這將是一個局部堆棧分配的塊,並且該方法將返回到該堆棧內存,這當然是很危險的一個參考。在這種情況下,代碼甚至仍然可以工作,即使它位於無主堆棧內存中(只要該內存在您調用該塊時尚未被其他人刪除),您仍可以調用該代碼。

我相信你的第二個例子也展示了堆棧分配塊的副作用。當堆被第一次實際複製到堆時,堆棧分配塊所捕獲的對象將僅被保留。這不會在您的代碼中發生,因此obj不會被該塊保留。

這意味着在您的示例中調用[obj release]後,該塊現在捕獲懸掛指針。使用ARC將爲您解決所有這些混亂的細節。

查看How blocks are implemented (and the consequences)(Cocoa With Love)瞭解更多詳情。

編輯:另外,強制性鏈接when to use retainCount

+1

+1,我同意分析。第一個片段是巧合工作,不應該依賴。關於'retainCount','NSMallocBlock永遠不會實際複製鏈接文章的段落「解釋了原因:它不會因爲實現而增加。另外值得一提的是:http://whentouseretaincount.com/ P –

+0

這並不是說該塊在堆棧中,它也可能是全局的。 –

+0

第一個例子中的塊肯定是全局的。 – Sulthan

3

爲什麼這個代碼片段的工作?

第一文檔片斷工作,因爲塊實施不具有參照周邊範圍,因此它是由鐺配置爲全球塊。這會導致塊不像通常那樣在堆棧上。

myBlock應該被銷燬。

堆棧幀(及其局部變量)沒有得到破壞。他們的記憶可用於進一步分配。無論如何,由於你的例子很簡單,你會得到一個不在棧幀中的全局塊。

如果您正在對周圍範圍進行任何引用,那麼您將擁有基於堆棧的塊,並且myBlock本來就是指向擁有內存位置的懸掛指針,可能導致崩潰。

[obj retainCount] in block print 1 instead 2,why?

聽起來很合理。你正在分配一個對象(保留計數:1),該塊保留它(保留計數:2),你釋放它(保留計數:1),該塊最終被執行(保留計數仍然爲1) 。

作爲一般說明,無論如何,在推理內存時不要依賴retainCount。內存管理過程有很多事情正在進行,只有通過查看retainCount的值才能明確說出任何內容,這是因爲您無法考慮實施的內部細節。更多關於這裏的主題:http://whentouseretaincount.com/

+0

感謝您對關於retainCount的解釋 – Vladimir

+2

第一個代碼片段不能巧合使用。嘗試將大小足夠大的變量推入堆棧,以覆蓋所分配的堆棧塊的內存,代碼仍然可以工作。因爲這個塊是全局的,而不是在堆棧上。 –

+2

@雷米我糾正了。修改我的答案,謝謝 –