Цель 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 ответа:
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
захватывается в обоих случаях. Компилятор не гарантирует перехват всех случаев сохранения циклов.