Виправлення застереження "Сильно захоплення [об'єкта] в цьому блоці, ймовірно, призведе до циклу збереження" в коді з підтримкою ARC


141

Як увімкнути код 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
Ви, ймовірно, використовуєте responseDataзамість цього rawResponseData, перевірте документацію ASIHTTPRequest.
0xced

Відповіді:


165

Відповідаючи собі:

Моє розуміння документації говорить про те, що використання ключового слова 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. Така ж поведінка, але вказівник залишається звисаючим, а не автоматично встановлюється на нуль при знищенні об'єкта.


8
На основі документів ARC звучить так, що вам потрібно використовувати __unsafe_unretain __block разом, щоб отримати таку поведінку, як і раніше, коли використовуєте ARC та блоки.
Мисливець

4
@SeanClarkHess: Коли я комбіную перші два рядки, я отримую це попередження: "Присвоєння збереженого об'єкта слабкій змінній; об'єкт буде випущено після призначення"
Гійом

1
@Guillaume дякую за відповідь, дехто, як я не помітив тимчасову змінну, спробував це, і попередження пропали. Чи знаєте ви, чому це працює? Це просто обмацувати компілятор, щоб придушити попередження, або попередження насправді більше не діє?
Кріс Вагнер

2
Я відправив простеження питання: stackoverflow.com/questions/8859649 / ...
barfoon

3
Чи може хтось пояснити, для чого потрібні ключові слова __block та __weak? Я думаю, що створюється цикл збереження, але я цього не бачу. І як створення тимчасової змінної виправляє проблему?
користувач798719

50

Проблема виникає тому, що ви призначаєте блок запиту, який має чітке посилання на запит. Блок автоматично збереже запит, тому вихідний запит не буде розміщений через цикл. Мати сенс?

Це просто дивно, оскільки ви позначаєте об’єкт запиту __block, щоб він міг посилатися на себе. Ви можете виправити це, створивши поруч слабке посилання .

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

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

__слаб ASIHTTPRequest * wrequest = запит; не працював для мене. Помилка надання я використав __block ASIHTTPRequest * blockRequest = запит;
Рам Г.

13

Це викликано завдяки збереженню "Я" в блоці. Доступ до блоку можна отримати з "Я", а "Я" передається в блок. це створить цикл утримування.

Спробуйте вирішити це, створивши слабку реферат 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];

Це правильна відповідь, і її слід зазначити як таку
Бенджамін

6

Іноді у компілятора xcode виникають проблеми з ідентифікатором циклів збереження, тому якщо ви впевнені, що ви не зберегли завершенняBlock, ви можете поставити прапор компілятора таким чином:

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

-(void)someMethod {
}

1
Деякі можуть стверджувати, що це поганий дизайн, але я іноді створюю незалежні об'єкти, які зависають в пам'яті, поки вони не закінчуються асинхронним завданням. Вони зберігаються властивістю fillBlock, яка містить чітке посилання на себе, створюючи навмисний цикл збереження. Блок завершення містить self.completionBlock = нуль, який звільняє завершення і порушує цикл збереження, дозволяючи звільнити об'єкт із пам'яті, коли завдання виконане. Ваша відповідь корисна, щоб допомогти заглушити попередження, які виникають, коли я це роблю.
гіперспазм

1
чесно кажучи, шанси на те, що хтось має рацію, а компілятор помиляється, дуже малий. Тож я б сказав, що просто перевершити попередження - це ризикована справа
Макс МакЛейд

3

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

Зауважте, що не використовуйте __weak, але __unsafe_unretention, оскільки моя мета - 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];        
}];

Цікаве рішення. Ви зрозуміли, чому він виходить з ладу в режимі випуску, а не в налагодженні?
Валеріо Сантінеллі


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.