Objective-C中的递归块在ARC中泄漏

时间:2012-01-13 21:30:24

标签: objective-c instruments automatic-ref-counting objective-c-blocks

所以我正在使用递归块。我理解,对于要递归的块,它需要以__block关键字开头,并且必须将其复制,以便将其放在堆上。但是,当我这样做时,它显示为仪器中的泄漏。有谁知道为什么或如何绕过它?

请注意,在下面的代码中我引用了很多其他块,但没有一个是递归的。

__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);

我尝试过将__block关键字用于Log()并尝试复制它,但它仍然会泄漏。有什么想法吗?

更新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);

当然有更好的方法吗?

3 个答案:

答案 0 :(得分:11)

好的,我自己找到了答案......但感谢那些试图提供帮助的人。

如果您正在引用/使用递归块中的其他块,则必须将它们作为弱变量传递。当然,__ weak仅适用于块指针类型,因此您必须先键入它们。这是最终的解决方案:

    typedef void (^IntBlock)(int);

    IntBlock __weak Log = ^(int i){
        NSLog(@"log sub %i", i);
    };

    __block void (^LeakingBlock)(int) = ^(int i){
        Log(i);
        if (i < 100) LeakingBlock(++i);
    };
    LeakingBlock(1);

以上代码不会泄漏。

答案 1 :(得分:0)

亚伦

由于您的代码似乎是单线程的,为什么要复制该块?如果你不复制该块,你就不会有泄漏。

安德鲁

答案 2 :(得分:-1)

如果没有进一步的背景信息,我可以这样说:

您正在泄漏该块,因为您正在复制它而不是将其释放到其他位置。您需要复制它以将其移动到堆,这没关系。但你选择的方式并不完全正确。

正确的方法是将其存储为某个对象实例变量,复制它,然后在dealloc中释放它。至少,这是一种不泄漏的方法。