這會導致任何類型的保留週期嗎?使用安全嗎?遞歸塊保留週期
__block void (^myBlock)(int) = [^void (int i)
{
if (i == 0)
return;
NSLog(@"%d", i);
myBlock(i - 1);
} copy];
myBlock(10);
myBlock = nil;
這會導致任何類型的保留週期嗎?使用安全嗎?遞歸塊保留週期
__block void (^myBlock)(int) = [^void (int i)
{
if (i == 0)
return;
NSLog(@"%d", i);
myBlock(i - 1);
} copy];
myBlock(10);
myBlock = nil;
您的代碼確實包含保留週期,但你可以打破在遞歸的終點由遞歸基本情況(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
),改「控制檯」到「對象列表」,並粘貼地址轉換成搜索框。儀器顯示,我只是塊分配:
實時列下的圓點表示退出程序塊時仍然分配。它被泄露。我點擊箭頭地址旁邊看到塊分配的全部歷史:
在本配置都發生只有一件事:它被分配。
接下來,我取消註釋if (i == 0)
聲明中的myBlock = nil
行。然後我再次在探查器下面運行它。爲了安全起見,系統隨機化了內存地址,所以我清除了搜索欄,然後再次檢查控制檯以查找此次運行中該塊的地址。這次是0x7fc7a1424700
。我再次切換到「對象列表」視圖並粘貼在新地址0x7fc7a1424700
中。這是我看到:
有沒有點實時列下這一次,這意味着該塊已被程序退出時釋放。然後我點擊箭頭上的地址旁看到完整的歷史:
這一次,塊被分配,釋放和解脫。
在這種情況下,即使它具有* __ block *存儲說明符,爲什麼該塊會強烈捕獲? –
@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限定。」 –
@RamyAlZuhouri因此,在ARC下,__block沒有顯式所有權限定符的變量是合格的'__strong'並保留它引用的對象。 –
如果您使用ARC,則有一個保留週期,因爲__block
對象變量由塊保留。所以該塊保留自己。您可以通過將myBlock
同時標爲__block
和__weak
來避免它。
如果您使用MRC,__block
對象變量不保留,並且您應該沒有問題。請記住在最後發佈myBlock
。
有避免的週期和潛在需要一個簡單的解決過早複製:
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);
這變得乏味如果你想擴展它來做相互遞歸,但我想不出一個很好的理由來做到這一點(不會更清楚一類)。
真棒解決方案,非常適合多次重複動畫 – Lucien
我想一個解決方案,就沒有得到警告,並在此線程https://stackoverflow.com/a/17235341/259521 Tammo福瑞斯提供了最好的解決辦法:
__block void (__weak ^blockSelf)(void);
void (^block)(void) = [^{
// Use blockSelf here
} copy];
blockSelf = block;
// Use block here
他的解釋非常有意義。
這裏是一個現代的解決問題的辦法:
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);
我已經發布了一個簡單的方法來解決保留遞歸塊週期。這也適用於ARC:http://stackoverflow.com/a/14730061/439096 – Berik