这是否会导致任何形式的保留周期? 是否可以安全使用?
__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;
您的代码不包含保留周期,但你可以打破在递归的终点通过设置保留周期myBlock
在递归基本情况为零( i == 0
)。
为了证明这一点的最好办法是尝试吧,分配仪器下运行,用“放弃时停止未记录数据”关闭“记录的引用计数”开启,“只跟踪活动分配”关闭。
我创建使用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;
}
然后,我跑到它分配仪器下,与我上述的设置。 然后,我在仪器改“统计”到“控制台”,看看该程序的输出:
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
),改“控制台”到“对象列表”,并粘贴地址到搜索框中。 仪器显示,我只是块分配:
实时栏下点意味着这个程序退出时该块还在分配。 它被泄露。 我点击旁边看到块分配的完整历史记录的地址的箭头:
只有一件事都发生在本配置:它被分配。
接着我未注释的myBlock = nil
的线if (i == 0)
语句。 然后,我又跑了它的探查下。 系统会随机内存地址的安全性,所以我清空了搜索栏,然后再检查控制台块的这个运行地址。 这是0x7fc7a1424700
这个时候。 我又贴切换到“对象列表”视图中的新地址, 0x7fc7a1424700
。 这是我看到:
有实时栏下没有点这个时间,这意味着该块已被程序退出时释放。 然后我点击旁边看到完整的历史地址的箭头:
这一次,块被分配,释放和解脱。
有避免循环,并过早地复制的潜在需要一个简单的解决方案:
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);
如果你想扩展它做相互递归这变得乏味,但我想不出一个很好的理由摆在首位做到这一点(不会一类更清楚?)。
如果你正在使用ARC,你有保有周期,是因为__block
对象变量由块保留。 所以,该块保留本身。 您可以通过声明避免myBlock
既是__block
和__weak
。
如果您正在使用MRC, __block
对象变量没有保留下来,您应该没有问题。 不过,别忘了释放myBlock
结尾。
不,这不会引起保留周期。 该__block
关键字告诉块不可复制myBlock
,这将分配之前导致应用程序崩溃时有发生。 如果这不是圆弧,你需要做的唯一事情是释放myBlock
你打电话后myBlock(10)
我想一个解决方案,就没有得到警告,并在此线程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);