Управление памятью Objective C с блоками, ARC и без ARC
Вопрос
Я использую блоки уже некоторое время, но чувствую, что есть вещи, которые мне не хватает в управлении памятью как в ARC, так и в средах без ARC.Я чувствую, что более глубокое понимание поможет мне устранить многие утечки памяти.
AFNetworking — это мое основное использование блоков в конкретном приложении.Большую часть времени внутри обработчика завершения операции я делаю что-то вроде «[self.myArray addObject]».
Как в средах с поддержкой ARC, так и без нее, «self» будет сохраняться в соответствии с эта статья от Apple.
Это означает, что всякий раз, когда вызывается блок завершения сетевой операции AFNetworking, self сохраняется внутри этого блока и освобождается, когда этот блок выходит за пределы области действия.Я считаю, что это относится как к ARC, так и к не-ARC.Я запустил инструмент Leaks и статический анализатор, чтобы найти любые утечки памяти.Никто ничего не показал.
Однако лишь недавно я наткнулся на предупреждение, которое не мог понять.В этом конкретном примере я использую 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 жалуется на строку, вызывающую errorBlock, с сообщением «Сильный захват «self» в этом блоке, скорее всего, приведет к циклу сохранения.Я считаю, что Xcode прав:блок отказа сохраняет себя, а сам — свою копию блока, поэтому ни один из двух не будет освобожден.
Однако у меня есть следующие вопросы/наблюдения.
1) Если я изменю _failureBlock(error) на «self.failureBlock(error)» (без кавычек), компилятор перестанет жаловаться.Почему это?Это утечка памяти, которую пропускает компилятор?
2) В общем, как лучше всего работать с блоками как в средах с поддержкой ARC, так и без нее при использовании блоки, являющиеся переменными экземпляра?Кажется, что в случае блоков завершения и сбоя в AFNetworking эти два блока нет переменные экземпляра, поэтому они, вероятно, не попадают в категорию циклов сохранения, которые я описал выше.Но что можно сделать при использовании блоков прогресса в AFNetworking, чтобы избежать циклов сохранения, подобных приведенному выше?
Мне бы хотелось услышать мысли других людей об ARC и не-ARC с блоками и проблемах/решениях с управлением памятью.Я считаю, что такие ситуации склонны к ошибкам, и считаю, что необходимо некоторое обсуждение этого вопроса, чтобы прояснить ситуацию.
Не знаю, имеет ли это значение, но я использую Xcode 4.4 с последней версией LLVM.
Решение
1) Если я изменяю _failureblock (ошибка) на "self.failureblock (ошибка)" (без кавычек) компилятор останавливает жалобу. Это почему? Это Утечка памяти компилятор пропускает?
Цикл сохранения существует в обоих случаях. Если вы нацеливаете iOS 5+, вы можете пройти со слабым ссылкой на себя:
__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");
}];
.
Сейчас я не будет сохранен, и если он будет отделен до того, как вызывается обратный вызов, обратный вызов - это NO-OP. Тем не менее, возможно, что он может пройти откидность, в то время как обратный вызов вызывается на фоновой резьбе, поэтому этот шаблон может создать случайные аварии.
2) в целом, какова лучшая практика для работы с блоками в обоих Среды с поддержкой дуги и без дуги при использовании блоков Переменные экземпляра? Кажется, что в случае завершения и неудачи Блоки в Afnetworking, эти два блока не являются переменными экземпляра, так Они, вероятно, не попадают в категорию сохранения циклов, которые я описано выше. Но при использовании блоков прогресса в AfnetWorking, Что можно сделать, чтобы избежать сохранения циклов, как то выше?
Большую часть времени, я думаю, Это лучше не Блоки хранения в экземплярах переменные . Если вы вместо этого возвращаете блок из способа в своем классе, у вас все еще будет сохранить цикл сохранения, но он существует только с момента, когда этот метод будет выпущен в момент, когда блок выпускается. Таким образом, он предотвратит выделение вашего применения во время выполнения блока, но цикл сохранения заканчивается, когда блок выпускается:
-(SFCompletionBlock)completionBlock {
return ^(AFHTTPRequestOperation *operation , id responseObject ) {
[self doSomethingWithOperation:operation];
};
}
[self.operation setCompletionBlockWithSuccess:[self completionBlock]
failure:[self failureBlock]
];
. Другие советы
Это означает, что всякий раз, когда называется блок завершения операции сети Afnetworking, Self сохраняется внутри этого блока и выпускается, когда этот блок выходит из сферы действия.
Нет, self
сохраняется в блоке при его создании.И он освобождается, когда блок освобождается.
Я считаю, что Xcode прав:Блок отказа сохраняет себя, и Self удерживает свою собственную копию блока, поэтому ни один из двух не будет сделкой.
Рассматриваемый блок, который сохраняет self
блок завершения передается setCompletionBlockWithSuccess
. self
не содержит ссылки на этот блок.Скорее, self.operation
(вероятно, что-то вроде NSOperation
) сохраняет блоки во время выполнения.Таким образом, существует временный цикл.Однако когда операция завершится выполнением, цикл будет разорван.
1) Если я изменю _failureBlock (ошибка) на «self.failureBlock (ошибка)» (без кавычек) Компилятор прекращает жаловаться.Почему это?Это утечка памяти, которую пропускает компилятор?
Разницы быть не должно. self
фиксируется в обоих случаях.Компилятор не гарантирует, что он уловит все случаи циклов сохранения.