这会导致任何类型的保留周期吗?使用安全吗?
__block void (^myBlock)(int) = [^void (int i)
{
if (i == 0)
return;
NSLog(@"%d", i);
myBlock(i - 1);
} copy];
myBlock(10);
myBlock = nil;
答案 0 :(得分:34)
您的代码确实包含保留周期,但您可以通过在递归基本情况下将myBlock
设置为nil来在递归结束时中断保留周期({{1} })。
证明这一点的最佳方法是尝试在分配工具下运行,“停止时丢弃未记录的数据”关闭,“记录参考计数”打开,“仅跟踪活动分配”关闭。 / p>
我使用OS X命令行工具模板创建了一个新的Xcode项目。这是整个计划:
i == 0
然后我使用上面描述的设置在Allocations仪器下运行它。然后我在Instruments中将“Statistics”更改为“Console”,以查看程序输出:
#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;
}
我复制了阻止地址(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>
),将“控制台”更改为“对象列表”,并将地址粘贴到搜索框中。仪器向我展示了块的分配:
Live列下的点表示程序退出时仍然分配了块。它被泄露了。我单击地址旁边的箭头以查看块分配的完整历史记录:
这个分配只发生过一件事:它被分配了。
接下来,我在0x7ff142c24700
语句中取消注释了myBlock = nil
行。然后我再次在探查器下运行它。系统随机化内存地址以确保安全性,因此我清除了搜索栏,然后再次检查控制台以查看此运行中的块地址。这次是if (i == 0)
。我再次切换到“对象列表”视图并粘贴到新地址0x7fc7a1424700
中。这就是我所看到的:
这次Live列下没有任何点,这意味着程序退出时块已被释放。然后我点击地址旁边的箭头查看完整的历史记录:
这一次,块被分配,释放和释放。
答案 1 :(得分:14)
有一个简单的解决方案可以避免周期和潜在的过早复制需求:
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);
如果你想扩展它以进行相互递归,这就变得乏味了,但是我不能想到一个很好的理由这样做(不会让一个类更清楚吗?)。
答案 2 :(得分:2)
如果您使用ARC,则会有一个保留周期,因为块保留了__block
个对象变量。所以该块保留了自己。您可以通过将myBlock
声明为__block
和__weak
来避免这种情况。
如果您使用的是MRC,则不会保留__block
个对象变量,您应该没有问题。请记住最后发布myBlock
。
答案 3 :(得分:1)
不,这不会导致保留周期。 __block
关键字告诉块不要复制myBlock
,这会在分配导致应用程序崩溃之前发生。如果这不是ARC,那么您需要做的就是在致电myBlock
后发布myBlock(10)
。
答案 4 :(得分:1)
我想要一个没有警告的解决方案,在这个帖子中https://stackoverflow.com/a/17235341/259521 Tammo Freese提供了最佳解决方案:
__block void (__weak ^blockSelf)(void);
void (^block)(void) = [^{
// Use blockSelf here
} copy];
blockSelf = block;
// Use block here
他的解释很有道理。
答案 5 :(得分: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);