紧凑禁用自引用块的arc-retain-cycles警告

时间:2011-11-24 01:11:13

标签: objective-c objective-c-blocks clang automatic-ref-counting

我正在编写一个涉及事件处理的API,我希望能够为处理程序使用块。回调通常需要访问或修改self。在ARC模式下,Clang警告说引用self的块可能会创建一个保留周期,这似乎是一个有用的警告,我想继续保持。

但是,对于我的这部分API,回调的生命周期和包含对象在外部进行维护。我知道当对象应该被释放时我可以打破循环。

我可以使用#pragma clang diagnostic ignored "-Warc-retain-cycles"在每个文件的基础上关闭保留周期警告,但是会禁用整个文件的警告。我可以用#pragma clang diagnostic pushpop围绕那个警告来围绕这些街区,但这会让这些街区难看。

我还可以通过引用指向self的__weak变量而不是直接引用self来获取警告,但这会使块的使用变得不那么令人愉快。

我提出的最佳解决方案是这个宏在块周围执行诊断禁用:

#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 个答案:

答案 0 :(得分:7)

首先,有一种简单的方法可以使用#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 :(得分:1)

我认为禁用警告是目前唯一正确的方法,因为它说编译器:不关心这个保留周期,我知道它,我会自己处理观察者。 引入弱引用是一种代价高昂的解决方案,因为它带有运行时CPU和内存开销。

答案 2 :(得分:1)

我写了下面的宏,其中我认为,非常聪明......

#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 haranguing you ),例如..

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

你可以加上任何警告标志,而Clang会留意你的想法......

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

然后,警告通常是有原因的。党派人物。

答案 3 :(得分:0)

新的LLVM更好地检测/防止此类保留周期 等待LLVM与ios6一起发货,或者用alex创建一个弱变量。

禁用警告是个坏主意!

答案 4 :(得分:0)

为了解决创建弱引用的笨拙问题,我将其放入宏中。它使用预处理器来创建一个具有相同名称但带有前缀的新var(在这种情况下为“w”;我避免使用'弱',因为这样做会过度杀戮并且更多地使用大小写规则):

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

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

如果,otoh,弱引用是不可取的,请不要使用它!我喜欢nielsbot将对象作为参数传递的解决方案(当然可能的话)。