Цель c управление памятью с блоками, дуги и дуги


Я использую блоки уже некоторое время, но чувствую, что есть вещи, которые я пропускаю в управлении памятью как в дуговых, так и в НЕДУГОВЫХ средах. Я чувствую, что более глубокое понимание позволит мне устранить многие утечки памяти.

AFNetworking-это мое основное использование блоков в конкретном приложении. Большую часть времени внутри обработчика завершения операции я делаю что-то вроде "[self.myArray addObject]".

Как в дуговых, так и в НЕДУГОВЫХ средах "self" будет сохранен согласно этой статье от Apple .

Это означает, что всякий раз, когда вызывается блок завершения сетевой операции AFNetworking, self сохраняется внутри этого блока и освобождается, когда этот блок выходит за пределы области действия. Я считаю, что это относится как к дуге, так и к не-дуге. Я запустил инструмент утечки и статический анализатор, чтобы найти любые утечки памяти. Никто ничего не показал.

Однако, он не был до недавнего времени, что я наткнулся на предупреждение, что я не мог понять из. Я использую ARC в этом конкретном примере.

У меня есть две переменные экземпляра, которые указывают на завершение и сбой сетевой операции

@property (nonatomic, readwrite, copy) SFCompletionBlock completionBlock;
@property (nonatomic, readwrite, copy) SFFailureBlock failureBlock;
@synthesize failureBlock = _failureBlock;
@synthesize operation = _operation;

Где-то в коде я делаю так:

[self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id
                                                    responseObject) {
NSError *error = [NSError errorWithDomain:@"com.test" code:100 userInfo:@{@"description": @"zero results"}];
            _failureBlock(error);
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            NSLog(@"nothing");
        }];

Xcode жалуется на строку, которая вызывает failureBlock, с сообщением "захват" self " сильно в этом блоке, вероятно, приведет к циклу сохранения. Я считаю, что Xcode прав: блок сбоя сохраняет себя, а self держит свою собственную копию блока, поэтому ни один из двух будет освобожден.

Однако у меня есть следующие вопросы/замечания.

1) Если я изменю _failureBlock (error)на " self.failureBlock (error) " (без кавычек) компилятор перестает жаловаться. Почему это так? Это утечка памяти, которую пропускает компилятор?

2) В общем случае, какова наилучшая практика работы с блоками как в дуговых, так и в НЕДУГОВЫХ средах при использовании блоков , являющихся переменными экземпляра? Кажется, что в случае завершения и неудачи блоки в AFNetworking эти два блока являются переменными экземпляра , а не, поэтому они, вероятно, не попадают в категорию циклов сохранения, которые я описал выше. Но при использовании блоков прогресса в AFNetworking, что можно сделать, чтобы избежать циклов сохранения, подобных приведенному выше?

Я хотел бы услышать мысли других людей об ARC и non-ARC с блоками и проблемами / решениями с управлением памятью. Я нахожу эти ситуации склонными к ошибкам, и я чувствую, что некоторые дискуссии по этому вопросу необходимы, чтобы проясните ситуацию.

Я не знаю, имеет ли это значение, но я использую Xcode 4.4 с последним LLVM.

2 6

2 ответа:

1) Если я изменю _failureBlock (error)на " self.failureBlock (ошибка)" (без кавычек) компилятор перестает жаловаться. Почему это так? Это утечка памяти, которую пропускает компилятор?

Цикл удержания существует в обоих случаях. Если вы нацелены на iOS 5+, Вы можете передать слабую ссылку на self:

__weak MyClass *weakSelf;
[self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSError *error = [NSError errorWithDomain:@"com.test" code:100 userInfo:@{@"description": @"zero results"}];
    if (weakSelf.failureBlock) weakSelf.failureBlock(error);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"nothing");
}];

Теперь self не будет сохранен, и если он будет освобожден до вызова обратного вызова, обратный вызов не будет выполняться. освобождение при обратном вызове вызывается в фоновом потоке, поэтому этот шаблон может создавать случайные сбои.

2) В общем, как лучше всего работать с блоками в обоих случаях Среды с поддержкой дуги и без дуги при использовании блоков, которые переменные экземпляра? Кажется, что в случае завершения и неудачи блоки в AFNetworking эти два блока не являются переменными экземпляра, поэтому они, вероятно, не попадают в категорию циклов сохранения, которые я описанный выше. Но при использовании блоков прогресса в AFNetworking, что можно сделать, чтобы избежать сохранения циклов, подобных описанному выше?

Большую часть времени я думаю, что лучше не хранить блоки в переменных экземпляра. Если вместо этого вы возвращаете блок из метода в вашем классе, у вас все еще будет цикл сохранения, но он существует только с момента вызова метода до момента освобождения блока. Таким образом, это предотвратит освобождение вашего экземпляра во время выполнения блока, но цикл удержания заканчивается, когда блок освобождается:

-(SFCompletionBlock)completionBlock {
    return ^(AFHTTPRequestOperation *operation , id responseObject ) {
        [self doSomethingWithOperation:operation];
    };
}

[self.operation setCompletionBlockWithSuccess:[self completionBlock]
                                      failure:[self failureBlock]
];

Это означает, что всякий раз, когда блок завершения сети AFNetworking операция называется, самость удерживается внутри этого блока и освобождается когда этот блок выходит за пределы видимости.

Нет, self сохраняется блоком при его создании. И он освобождается, когда блок освобождается.

Я считаю, что Xcode прав: блок сбоя сохраняет себя и себя держит свою собственную копию блока, так что ни один из двух не будет освободившему.

Рассматриваемый блок, который сохраняет self, является блоком завершения, переданным setCompletionBlockWithSuccess. self не содержит ссылки на этот блок. Скорее, self.operation (предположительно, какой-то NSOperation) сохраняет блоки во время выполнения. Таким образом, временно существует цикл. Однако, когда операция будет выполнена, цикл будет прерван.

1) Если я изменю _failureBlock (error)на " self.failureBlock (ошибка)" (без кавычек) компилятор перестает жаловаться. Почему это? Это утечка памяти, которую пропускает компилятор?

Не должно быть никакой разницы. self захватывается в обоих случаях. Компилятор не гарантирует перехват всех случаев сохранения циклов.