Компактное отключение предупреждения цикла дуги для блоков саморегуляции
Я пишу API, который включает обработку событий, и я хотел бы иметь возможность использовать блоки для обработчиков. Обратные вызовы часто хотят получить доступ или изменить себя. В режиме ARC Clang предупреждает, что блоки, ссылающиеся на self, скорее всего, создадут цикл сохранения, который кажется полезным предупреждением, которое я хочу продолжать в целом.
Однако для этой части моего 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. Есть ли лучший способ отключить или избежать предупреждения?
Ответы
Ответ 1
Для начала существует простой способ отключить предупреждения для определенных строк кода с помощью #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
Ответ 2
Я думаю, что отключение предупреждения в настоящее время является единственным правильным способом, поскольку он говорит о компиляторе: не заботясь об этом цикле сохранения, я знаю об этом, и сам избавлюсь от наблюдателя.
Внедрение слабых ссылок - дорогостоящее решение, поскольку оно связано с нехваткой времени на процессор и памятью.
Ответ 3
новый LLVM лучше обнаруживает/предотвращает такие циклы удержания
дождитесь доставки LLVM с помощью ios6 или выполните alex с созданием слабого var.
отключение предупреждения - плохая идея, хотя!
Ответ 4
Я написал следующий макрос, который, я думаю, довольно умен...
#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
Затем снова предупреждения обычно существуют по какой-то причине. Сторонние попперы.
Ответ 5
Чтобы решить проблему неуклюжести создания слабой ссылки, я помещал ее в макрос. Он использует препроцессор для создания нового var с тем же именем, но с префиксом ( "w", в этом случае, я избегал "слабых", потому что это было бы излишним и беспорядок больше с правилами капитализации):
#define WEAK_VAR(NAME) __unsafe_unretained typeof(NAME) w##NAME = NAME
...
WEAK_VAR(self);
self.block = ^{
[wself doStuff];
};
Если, otoh, слабая ссылка нежелательна, не используйте ее! Мне нравится решение nielsbot о передаче объекта в качестве параметра (когда это возможно, конечно).