20

這會導致任何類型的保留週期嗎?使用安全嗎?遞歸塊保留週期

__block void (^myBlock)(int) = [^void (int i) 
{ 
    if (i == 0) 
     return; 

    NSLog(@"%d", i); 
    myBlock(i - 1); 
} copy]; 
myBlock(10); 

myBlock = nil; 
+1

我已經發布了一個簡單的方法來解決保留遞歸塊週期。這也適用於ARC:http://stackoverflow.com/a/14730061/439096 – Berik

回答

34

您的代碼確實包含保留週期,但你可以打破在遞歸的終點由遞歸基本情況(i == 0)設置myBlock爲零的保留週期。

要證明這一點,最好的方法是在分配工具下運行,關閉「關閉時丟棄未記錄數據」,打開「記錄引用計數」並關閉「僅跟蹤活動分配」。

我使用OS X命令行工具模板創建了一個新的Xcode項目。下面是整個程序:

#import <Foundation/Foundation.h> 

void test() { 
    __block void (^myBlock)(int) = [^void (int i){ 
     if (i == 0) { 
//   myBlock = nil; 
      return; 
     } 
     NSLog(@"myBlock=%p %d", myBlock, i); 
     myBlock(i - 1); 
    } copy]; 
    myBlock(10); 
} 

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     test(); 
    } 
    sleep(1); 
    return 0; 
} 

然後我在Allocations儀器下運行它,並使用上述設置。然後,我改變「統計」到「控制檯」在儀器,看節目輸出:

2012-10-26 12:04:31.391 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 10 
2012-10-26 12:04:31.395 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 9 
2012-10-26 12:04:31.396 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 8 
2012-10-26 12:04:31.397 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 7 
2012-10-26 12:04:31.397 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 6 
2012-10-26 12:04:31.398 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 5 
2012-10-26 12:04:31.398 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 4 
2012-10-26 12:04:31.399 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 3 
2012-10-26 12:04:31.400 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 2 
2012-10-26 12:04:31.401 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 1 
<End of Run> 

我複製的塊地址(0x7ff142c24700),改「控制檯」到「對象列表」,並粘貼地址轉換成搜索框。儀器顯示,我只是塊分配:

block leaked

實時列下的圓點表示退出程序塊時仍然分配。它被泄露。我點擊箭頭地址旁邊看到塊分配的全部歷史:

在本配置都發生

block leaked detail

只有一件事:它被分配。

接下來,我取消註釋if (i == 0)聲明中的myBlock = nil行。然後我再次在探查器下面運行它。爲了安全起見,系統隨機化了內存地址,所以我清除了搜索欄,然後再次檢查控制檯以查找此次運行中該塊的地址。這次是0x7fc7a1424700。我再次切換到「對象列表」視圖並粘貼在新地址0x7fc7a1424700中。這是我看到:

block freed

有沒有點實時列下這一次,這意味着該塊已被程序退出時釋放。然後我點擊箭頭上的地址旁看到完整的歷史:

block freed detail

這一次,塊被分配,釋放和解脫。

+1

在這種情況下,即使它具有* __ block *存儲說明符,爲什麼該塊會強烈捕獲? –

+0

@RamyAlZuhouri閱讀clang ARC文檔。 [7.5節](http://clang.llvm.org/docs/AutomaticReferenceCounting.html#blocks)說:「推理規則同樣適用於__block變量,這是從非ARC的語義轉變,其中__block變量沒有在捕獲期間隱式保留「。」推斷「是指向[§4.4]的鏈接(http://clang.llvm.org/docs/AutomaticReferenceCounting。html#ownership-inference),它說:「如果一個對象聲明爲保留對象所有者類型,但沒有明確的所有權限定符,則其類型隱式調整爲具有__strong限定。」 –

+0

@RamyAlZuhouri因此,在ARC下,__block沒有顯式所有權限定符的變量是合格的'__strong'並保留它引用的對象。 –

1

不,這不會導致保留週期。 __block關鍵字告訴該塊不復制myBlock,該分配在分配之前會發生,導致應用程序崩潰。如果這不是ARC,則只需致電myBlock(10)後,您需要執行的操作是釋放myBlock

+0

更多細節可以在這裏找到:http://www.friday.com/bbum/2009/08/29/blocks-tips-tricks/ – Joe

+0

那麼,我應該在第一次通話之後還是在最後一次通話之後再釋放它? –

+0

一旦你完成它。不要在塊內釋放它,在調用'myBlock(10)'後釋放。我補充說,答案。 – Joe

2

如果您使用ARC,則有一個保留週期,因爲__block對象變量由塊保留。所以該塊保留自己。您可以通過將myBlock同時標爲__block__weak來避免它。

如果您使用MRC,__block對象變量不保留,並且您應該沒有問題。請記住在最後發佈myBlock

13

有避免的週期和潛在需要一個簡單的解決過早複製:

void (^myBlock)(id,int) = ^(id thisblock, int i) { 
    if (i == 0) 
     return; 

    NSLog(@"%d", i); 
    void(^block)(id,int) = thisblock; 
    block(thisblock, i - 1); 
    }; 

myBlock(myBlock, 10); 

您可以添加一個包裝來獲得原始類型簽名回:

void (^myBlockWrapper)(int) = ^(int i){ return myBlock(myBlock,i); } 

myBlockWrapper(10); 

這變得乏味如果你想擴展它來做相互遞歸,但我想不出一個很好的理由來做到這一點(不會更清楚一類)。

+0

真棒解決方案,非常適合多次重複動畫 – Lucien

1

我想一個解決方案,就沒有得到警告,並在此線程https://stackoverflow.com/a/17235341/259521 Tammo福瑞斯提供了最好的解決辦法:

__block void (__weak ^blockSelf)(void); 
void (^block)(void) = [^{ 
     // Use blockSelf here 
} copy]; 
blockSelf = block; 
    // Use block here 

他的解釋非常有意義。

0

這裏是一個現代的解決問題的辦法:

void (^myBlock)(); 
__block __weak typeof(myBlock) weakMyBlock; 
weakMyBlock = myBlock = ^void(int i) { 
    void (^strongMyBlock)() = weakMyBlock; // prevents the block being delloced after this line. If we were only using it on the first line then we could just use the weakMyBlock. 
    if (i == 0) 
     return; 

    NSLog(@"%d", i); 
    strongMyBlock(i - 1); 
}; 
myBlock(10);