Компактное отключение предупреждения цикла дуги для блоков саморегуляции

Я пишу 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 о передаче объекта в качестве параметра (когда это возможно, конечно).