15

我在写一个涉及事件处理的API,我希望能够为处理程序使用块。回调通常需要访问或修改自我。在ARC模式中,Clang警告说,引用自我的块可能会创建一个保留周期,这似乎是我希望保持一般的有用警告。紧凑地禁用自引用块的弧保留周期警告

但是,对于我的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的编辑器交互不佳。有没有更好的方法来禁用或避免警告?

+10

创建'self'的__weak引用实际上需要一行代码。我认为在这种情况下解决问题要比试图缓解症状好。如何引用'weakSelf'而不是'self'使得块的使用更愉快? –

+3

它在一些方面不太愉快。听众往往很短,有时只是一个声明。 __weak声明使侦听器的大小加倍。这也意味着你需要限定财产访问而不是使用推断自我。我会同意我目前的解决方案可能比只使用__weak更糟糕,但我希望通过这个问题获得更好的解决方案。 –

+1

你可以改变你的完成块的原型来接受一个“自我”的论点吗?现在你传递块的代码看起来是一样的(除了接受一个额外的参数),你可以消除警告。 (即让您的API将相关对象传递给您的块) – nielsbot

回答

7

首先,存在使用#pragma禁用对某些代码行警告,一种简单的方式开始:

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

但我不会用它在这种特殊情况下,因为它不会解决问题,它只是将它从开发人员那里隐藏起来。 我宁愿选择Mark提出的解决方案。要创建一个弱引用,你可以做以下的块之外的一个:

__weak typeof(self) weakSelf = self; // iOS ≥ 5 
__unsafe_unretained typeof(self) unsafeUnretainedSelf = self; // 5 > iOS ≥ 4 
__block typeof(self) blockSelf = self; // ARC disabled 
0

新LLVM是在检测好/防止这样的保留周期 等待LLVM运输与iOS6的或做Alex的方式与创建一个弱变量。

虽然禁用警告是一个坏主意!

1

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

0

我写了下面的宏,这我认为,是相当聪明......

#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 

话又说回来,警告通常是有原因的。党的poppers。

0

为了解决创建弱引用的问题,我把它放到一个宏中。它使用预处理器来创建一个新的变种名称相同,但有一个前缀(“W”,在这种情况下,我避免“弱”,因为这将是矫枉过正,并用大写规则惹更多):

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

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

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