Recursive Blocks in Objective-C leaking in ARC

Итак, я использую рекурсивные блоки. Я понимаю, что для того, чтобы блок был рекурсивным, ему должно предшествовать ключевое слово __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); 

UPDATE В ходе собственных исследований я обнаружил, что проблема, по-видимому, действительно связана со ссылками на другие блоки, которые использует этот блок. Если я делаю что-то простое, как здесь, то утечки нет:

 __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(), а также пробовал копировать его, но утечка все равно происходит. Есть идеи?

UPDATE 2 Я нашел способ предотвратить утечку, но он несколько обременителен. Если я преобразую передаваемый блок в слабый идентификатор, а затем верну слабый идентификатор в тип блока, я смогу предотвратить утечку.

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

Наверняка есть лучший способ?

15
задан Aaron Hayman 17 January 2012 в 14:42
поделиться