Предупреждение об ошибке "Захват [объекта] сильно в этом блоке, вероятно, приведет к циклу сохранения" в коде с поддержкой 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?