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

7 136

7 ответов:

отвечая самому себе:

мое понимание документации говорит, что с помощью ключевого слова block и установка переменной в ноль после использования ее внутри блока должна быть в порядке, но она все еще показывает предупреждение.

__block ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
    request = nil;
// ....

    }];

обновление: получил его для работы с ключевым словом'_слаб' вместо ' _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. Тот же поведение, но указатель остается висящим, а не автоматически устанавливается в ноль, когда объект уничтожается.

проблема возникает, потому что вы назначаете блок для запроса, который имеет сильную ссылку на запрос в нем. Блок автоматически сохранит запрос, поэтому исходный запрос не будет освобожден из-за цикла. Есть смысл?

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

ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...];
__weak ASIHTTPRequest *wrequest = request;

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:wrequest.rawResponseData error:nil];
    // ...
    }];

это причины из-за сохранения себя в блоке. Блок будет доступен из self, и 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];

несколько раз компилятор xcode имеет проблемы с идентификатором циклов сохранения, поэтому, если вы уверены, что вы не сохраняете completionBlock, вы можете поместить флаг компилятора следующим образом:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"

-(void)someMethod {
}

когда я пробую решение, предоставленное Гийомом, все в порядке в режиме отладки, но он разбивается в режиме выпуска.

обратите внимание, что не используйте __слабый, но __небезопасный_unretained, потому что моя цель-iOS 4.3.

мой код аварийно завершает работу, когда setCompletionBlock: вызывается на объект "запрос": запрос был освобожден ...

таким образом, это решение работает как в режиме отладки, так и в режиме выпуска :

// 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];        
}];
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...
__block ASIHTTPRequest *blockRequest = request;
[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:blockRequest.responseData error:nil];
    blockRequest = nil;
// ....

}];

в чем разница между __слабой и __блочной ссылкой?

взгляните на документацию на веб-сайте разработчика Apple : https://developer.apple.com/library/prerelease/ios/#documentation/General/Conceptual/ARCProgrammingGuide/Introduction.html#//apple_ref/doc/uid/TP40011029

в нижней части страницы есть раздел о циклах сохранения.