【发布时间】:2012-02-10 01:34:54
【问题描述】:
所以我使用递归块。我知道要使块递归,它需要在前面加上 __block 关键字,并且必须将其复制以便可以放在堆上。但是,当我这样做时,它在 Instruments 中显示为泄漏。有人知道我为什么或如何解决它吗?
请注意,在下面的代码中,我引用了许多其他块,但它们都不是递归的。
__block NSDecimalNumber *(^ProcessElementStack)(LinkedList *, NSString *) = [^NSDecimalNumber *(LinkedList *cformula, NSString *function){
LinkedList *list = [[LinkedList alloc] init];
NSDictionary *dict;
FormulaType type;
while (cformula.count > 0) {
dict = cformula.pop;
type = [[dict objectForKey:@"type"] intValue];
if (type == formulaOperandOpenParen || type == formulaListOperand || type == formulaOpenParen) [list add:ProcessElementStack(cformula, [dict objectForKey:@"name"])];
else if (type == formulaField || type == formulaConstant) [list add:NumberForDict(dict)];
else if (type == formulaOperand) [list add:[dict objectForKey:@"name"]];
else if (type == formulaCloseParen) {
if (function){
if ([function isEqualToString:@"AVG("]) return Average(list);
if ([function isEqualToString:@"MIN("]) return Minimum(list);
if ([function isEqualToString:@"MAX("]) return Maximum(list);
if ([function isEqualToString:@"SQRT("]) return SquareRoot(list);
if ([function isEqualToString:@"ABS("]) return EvaluateStack(list).absoluteValue;
return EvaluateStack(list);
} else break;
}
}
return EvaluateStack(list);
} copy];
NSDecimalNumber *number = ProcessElementStack([formula copy], nil);
更新 因此,在我自己的研究中,我发现问题显然与对该块使用的其他块的引用有关。如果我做这样简单的事情,它就不会泄漏:
__block void (^LeakingBlock)(int) = [^(int i){
i++;
if (i < 100) LeakingBlock(i);
} copy];
LeakingBlock(1);
但是,如果我在其中添加另一个块,它确实会泄漏:
void (^Log)(int) = ^(int i){
NSLog(@"log sub %i", i);
};
__block void (^LeakingBlock)(int) = [^(int i){
Log(i);
i++;
if (i < 100) LeakingBlock(i);
} copy];
LeakingBlock(1);
我已经尝试对 Log() 使用 __block 关键字并尝试复制它,但它仍然泄漏。有什么想法吗?
更新 2 我找到了一种防止泄漏的方法,但这有点繁琐。如果我将传入的块转换为弱 id,然后将弱 id 转换回块类型,我可以防止泄漏。
void (^Log)(int) = ^(int i){
NSLog(@"log sub %i", i);
};
__weak id WeakLogID = Log;
__block void (^LeakingBlock)(int) = [^(int i){
void (^WeakLog)(int) = WeakLogID;
WeakLog(i);
if (i < 100) LeakingBlock(++i);
} copy];
LeakingBlock(1);
肯定有更好的方法吗?
【问题讨论】:
-
感谢您分享您的研究,我还没有听说过必须同时复制块。但是,似乎更新的 LLVM 在递归调用“在此块中强烈捕获 LeakingBlock”时发出警告可能会导致保留周期。我发现安抚编译器的唯一方法是对块使用一个单独的弱 ptr 有点类似于你下面的答案,尽管它不够实用,我很想在本地覆盖警告。一旦你尝试了最新的编译器,我很想看看你的看法。
-
@smallduck 最初,我使用了
copy,因为它会导致将块从堆栈复制到堆中。有一段时间效果很好,我也得到了编译器“递归”错误。我从我的代码中删除了copy(如我的回答中所反映)并且它有效(而以前我会得到EXC_BAD_ACCESS。我猜Apple更改了__block关键字以在堆上而不是在堆上创建块堆栈......但这只是一个猜测。 -
@smallduck 说实话,我已经放弃使用块进行递归了。是的,它可以做到,但它有点笨拙,并且有太多的陷阱。很容易以保留周期结束(这对递归来说真的很糟糕)并且变得难以阅读。所以通常我只是坚持使用方法/函数来进行递归。
标签: objective-c instruments automatic-ref-counting objective-c-blocks