您的代码确实包含一个保留周期,但您可以通过在递归基本情况 (i == 0) 中将 myBlock 设置为 nil 来打破递归结束时的保留周期。
证明这一点的最佳方法是尝试在 Allocations 工具下运行,关闭“Discard unrecorded data on stop”,打开“Record reference counts”,关闭“Only track active allocations”。
我使用 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 工具下运行它,并使用我上面描述的设置。然后我在 Instruments 中将“Statistics”改为“Console”,查看程序输出:
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),将“控制台”更改为“对象列表”,并将地址粘贴到搜索框中。 Instruments 向我展示了该块的分配情况:
Live 列下的点表示程序退出时该块仍被分配。它被泄露了。我点击了地址旁边的箭头,查看了区块分配的完整历史:
这个分配只发生过一件事:它被分配了。
接下来我取消了if (i == 0) 语句中的myBlock = nil 行的注释。然后我再次在分析器下运行它。为了安全起见,系统会随机分配内存地址,因此我清除了搜索栏,然后再次检查控制台以获取本次运行的块地址。这次是0x7fc7a1424700。我再次切换到“对象列表”视图并粘贴到新地址0x7fc7a1424700。这是我看到的:
这次 Live 列下没有点,这意味着程序退出时该块已被释放。然后我点击地址旁边的箭头查看完整的历史记录:
这一次,块被分配、释放和释放。