Предупреждение об ошибке "Захват [объекта] сильно в этом блоке, вероятно, приведет к циклу сохранения" в коде с поддержкой ARC
В коде с поддержкой ARC, как исправить предупреждение о потенциальном цикле удержания при использовании блочного API?
Предупреждение:
Capturing 'request' strongly in this block is likely to lead to a retain cycle
созданный этим фрагментом кода:
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...
[request setCompletionBlock:^{
NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.rawResponseData error:nil];
// ...
}];
Предупреждение связано с использованием объекта request
внутри блока.
Ответы
Ответ 1
Отвечая на вопрос:
Мое понимание документации гласит, что использование ключевого слова block
и установка переменной в nil после ее использования внутри блока должно быть в порядке, но оно все еще показывает предупреждение.
__block ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...
[request setCompletionBlock:^{
NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
request = nil;
// ....
}];
Обновление: заставил его работать с ключевым словом "_weak" вместо "_block" и с использованием временной переменной:
ASIHTTPRequest *_request = [[ASIHTTPRequest alloc] initWithURL:...
__weak ASIHTTPRequest *request = _request;
[request setCompletionBlock:^{
NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
// ...
}];
Если вы хотите также настроить iOS 4, используйте __unsafe_unretained
вместо __weak
. То же поведение, но указатель остается свисающим, а не автоматически устанавливается на нуль при уничтожении объекта.
Ответ 2
Проблема возникает из-за того, что вы назначаете блок для запроса, который имеет сильную ссылку на запрос в нем. Блок автоматически сохранит запрос, поэтому исходный запрос не будет освобожден из-за цикла. Есть смысл?
Это просто странно, потому что вы помечаете объект запроса __block, чтобы он мог ссылаться на себя. Вы можете исправить это, создав слабую ссылку рядом с ней.
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...];
__weak ASIHTTPRequest *wrequest = request;
[request setCompletionBlock:^{
NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:wrequest.rawResponseData error:nil];
// ...
}];
Ответ 3
Это связано с тем, что в блоке сохраняется "я". Блок будет получать доступ к себе, а self - в блоке. это создаст цикл сохранения.
Попробуйте решить эту проблему, создав слабую ссылку self
__weak typeof(self) weakSelf = self;
operationManager = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operationManager.responseSerializer = [AFJSONResponseSerializer serializer];
[operationManager setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[weakSelf requestFinishWithSucessResponseObject:responseObject withAFHTTPRequestOperation:operation andRequestType:eRequestType];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[weakSelf requestFinishWithFailureResponseObject:error withAFHTTPRequestOperation:operation andRequestType:eRequestType];
}];
[operationManager start];
Ответ 4
В некоторых случаях компилятор xcode имеет проблемы для идентификатора циклов сохранения, поэтому, если вы уверены, что не сохраняете завершение, вы можете поместить флаг компилятора следующим образом:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
-(void)someMethod {
}
Ответ 5
Когда я пытаюсь решить решение, предоставленное Гийомом, все в порядке в режиме отладки, но он падает в режиме Release.
Обратите внимание: не используйте __weak, а __unsafe_unretained, потому что моя цель - iOS 4.3.
Мой код сбой при установкеCompletionBlock: вызывается на объект "запрос": запрос был освобожден...
Итак, это решение работает как в режимах Debug, так и Release:
// Avoiding retain cycle :
// - ASIHttpRequest object is a strong property (crashs if local variable)
// - use of an __unsafe_unretained pointer towards self inside block code
self.request = [ASIHttpRequest initWithURL:...
__unsafe_unretained DataModel * dataModel = self;
[self.request setCompletionBlock:^
{
[dataModel processResponseWithData:dataModel.request.receivedData];
}];
Ответ 6
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...
__block ASIHTTPRequest *blockRequest = request;
[request setCompletionBlock:^{
NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:blockRequest.responseData error:nil];
blockRequest = nil;
// ....
}];
Какая разница между ссылками __weak и __block?
Ответ 7
Взгляните на документацию на веб-сайте разработчика Apple: https://developer.apple.com/library/prerelease/ios/#documentation/General/Conceptual/ARCProgrammingGuide/Introduction.html#//apple_ref/doc/uid/TP40011029
В нижней части страницы есть раздел о циклах сохранения.