Компактное отключение предупреждения о циклах сохранения дуги для блоков со ссылками на себя - PullRequest
15 голосов
/ 24 ноября 2011

Я пишу API, который включает обработку событий, и я хотел бы иметь возможность использовать блоки для обработчиков. Обратные вызовы часто хотят получить доступ или изменить себя. В режиме ARC Clang предупреждает, что блоки, ссылающиеся на себя, могут создать цикл сохранения, что кажется полезным предупреждением, которое я хочу сохранить в общем.

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

Я могу отключить предупреждение о сохранении цикла для каждого файла с помощью #pragma clang diagnostic ignored "-Warc-retain-cycles", но это отключает предупреждение для всего файла. Я могу окружить блоки #pragma clang diagnostic push и pop вокруг этого предупреждения, но это делает блоки некрасивыми.

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

Лучшее решение, которое я придумал, это макрос, который отключает диагностику вокруг блока:

#define OBSERVE(OBJ, OBSERVEE, PATH, CODE) \
[(OBJ) observeObject:(OBSERVEE) forKeyPath:(PATH) withBlock:^(id obj, NSDictionary *change) { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-retain-cycles\"") \
do { CODE; } while(0); \
_Pragma("clang diagnostic pop") \
}];

Это работает, но не очень доступно для пользователей API, оно не допускает вложенных наблюдателей и плохо взаимодействует с редактором XCode. Есть ли лучший способ отключить или избежать предупреждения?

Ответы [ 5 ]

7 голосов
/ 05 августа 2012

Для начала, есть простой способ отключить предупреждения для определенных строк кода, используя #pragma:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "<#A warning to ignore#>"
<#Code that issues a warning#>
#pragma clang diagnostic pop

Но я бы не стал использовать его в данном конкретном случае, потому что он неисправить проблему, она просто скрыт от разработчика.Я предпочел бы пойти с решением, которое предложил Марк.Чтобы создать слабую ссылку, вы можете выполнить одно из следующих действий за пределами блока:

__weak typeof(self) weakSelf = self; // iOS ≥ 5
__unsafe_unretained typeof(self) unsafeUnretainedSelf = self; // 5 > iOS ≥ 4
__block typeof(self) blockSelf = self; // ARC disabled
1 голос
/ 25 сентября 2013

Я написал следующий макрос, который я думаю , довольно умный ...

#define CLANG_IGNORE_HELPER0(x) #x
#define CLANG_IGNORE_HELPER1(x) CLANG_IGNORE_HELPER0(clang diagnostic ignored x)
#define CLANG_IGNORE_HELPER2(y) CLANG_IGNORE_HELPER1(#y)

#define CLANG_POP _Pragma("clang diagnostic pop")
#define CLANG_IGNORE(x)\
    _Pragma("clang diagnostic push");\
    _Pragma(CLANG_IGNORE_HELPER2(x))

Это позволяет вам делать все виды забавных вещей (без Xcode преследующих вас ), таких как ..

CLANG_IGNORE(-Warc-retain-cycles)
[object performBlock:^(id obj){ [obj referToSelfWithoutWarning:self]; }];
CLANG_POP

Вы можете поставить любой предупреждающий флаг, и Кланг прислушается к вашим прихотям ...

CLANG_IGNORE(-Warc-performSelector-leaks);
return [self performSelector:someIllBegotSelector withObject:arcFauxPas];
CLANG_POP

Опять же, предупреждения обычно существуют по какой-то причине. Вечеринка попперс.

1 голос
/ 20 декабря 2012

Я думаю, что отключение предупреждения в настоящее время является единственно правильным способом, так как он говорит компилятору: не заботьтесь об этом цикле сохранения, я знаю об этом и буду распоряжаться наблюдателем сам. Представление слабого эталона является дорогостоящим решением, так как оно поставляется с ЦП во время выполнения и накладными расходами памяти.

0 голосов
/ 26 ноября 2014

Чтобы решить проблему грубости создания слабой ссылки, я поместил это в макрос.Он использует препроцессор для создания новой переменной с тем же именем, но с префиксом (в данном случае 'w'; я избегал 'слабых', потому что это было бы излишним и запутало бы правила капитализации):

#define WEAK_VAR(NAME) __unsafe_unretained typeof(NAME) w##NAME = NAME

...
WEAK_VAR(self);
self.block = ^{
    [wself doStuff];
};

Если, ой, слабая ссылка нежелательна, не используйте ее!Мне нравится решение nielsbot о передаче объекта в качестве параметра (когда это возможно, конечно).

0 голосов
/ 16 сентября 2012

новый LLVM лучше обнаруживает / предотвращает такие циклы сохранения дождитесь доставки LLVM с помощью ios6 или сделайте Алекс, создав слабую переменную.

отключение предупреждения - плохая идея!

...