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

时间:2021-04-22 14:21:31

I'm writing an API that involves event handling, and I'd like to be able to use blocks for the handlers. The callbacks will often want to access or modify self. In ARC mode, Clang warns that blocks referencing self are likely to create a retain cycle, which seems like a helpful warning that I want to keep on in general.

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

However, for this portion of my API, the lifecycle of the callback and the containing object are maintained externally. I know I can break the cycle when the object should be deallocated.

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

I can turn off the retain cycle warning on a per file basis with #pragma clang diagnostic ignored "-Warc-retain-cycles", but that disables the warning for the entire file. I can surround the blocks with a #pragma clang diagnostic push and pop around that warning, but that makes the blocks ugly.

我可以在每个文件的基础上关闭保留周期警告,#pragma clang diagnostic忽略“-Warc-retain-cycles”,但是会禁用整个文件的警告。我可以使用#pragma clang诊断推送来围绕这些块并弹出该警告,但这会使块变得难看。

I can also get the warning to go away by referencing a __weak variable pointing to self instead of referencing self directly, but that makes the blocks far less pleasant to use.

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

The best solution I've come up with is this macro that does the diagnostic disabling around the block:

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

#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") \
}];

That works, but it's not very discoverable for API users, it doesn't allow nested observers, and it interacts poorly with XCode's editor. Is there a better way to disable or avoid the warning?

这可行,但它对于API用户来说不是很容易被发现,它不允许嵌套的观察者,并且它与XCode的编辑器交互不良。有没有更好的方法来禁用或避免警告?

5 个解决方案

#1


7  

To begin with, there is a simple way to disable warnings for certain lines of code using #pragma:

首先,有一种简单的方法可以使用#pragma禁用某些代码行的警告:

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

But I wouldn't use it in this particular case because it won't fix the issue, it'll just hide it from the developer. I would rather go with solution that Mark proposed. To create a weak reference, you can do one of the following outside of the block:

但我不会在这种特殊情况下使用它,因为它不能解决问题,它只是将它隐藏在开发人员之外。我宁愿选择马克提出的解决方案。要创建弱引用,可以在块外部执行以下操作之一:

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

#2


1  

I think disabling the warning is currently the only correct way to do, since it says the compiler: Do not care about this retain cycle, I am aware of it and I will dispose of the observer myself. Introducing a weak reference is a costly solution since it comes with runtime CPU and memory overhead.

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

#3


0  

the new LLVM is better at detecting/preventing such retain cycles wait for LLVM shipping with ios6 or do alex's way with creating a weak var.

新的LLVM更好地检测/防止这样的保留周期等待LLVM与ios6一起发货,或者在创建弱变量时采用alex的方式。

disabling the warning is a bad idea though!

禁用警告是一个坏主意!

#4


0  

I wrote the following macro, which I think, is pretty clever...

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

#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))

It allows you to do all sorts of fun things (without Xcode haranguing you), such as..

它允许你做各种有趣的事情(没有Xcode haranguing你),如..

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

You can put in any warning flag and Clang will heed to your whims...

你可以放入任何警告标志,Clang会留意你的想法......

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

Then again, the warnings usually are there for a reason. Party poppers.

然后,警告通常是有原因的。派对poppers。

#5


0  

To solve the problem of the clunkiness of creating a weak reference, I put that into a macro. It uses the preprocessor to create a new var with the same name but with a prefix ('w', in this case; I avoided 'weak' because that would be overkill and mess more with capitalisation rules):

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

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

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

If, otoh, a weak reference is not desirable, don't use it! I like nielsbot solution of passing the object as a parameter (when possible, of course).

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

#1


7  

To begin with, there is a simple way to disable warnings for certain lines of code using #pragma:

首先,有一种简单的方法可以使用#pragma禁用某些代码行的警告:

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

But I wouldn't use it in this particular case because it won't fix the issue, it'll just hide it from the developer. I would rather go with solution that Mark proposed. To create a weak reference, you can do one of the following outside of the block:

但我不会在这种特殊情况下使用它,因为它不能解决问题,它只是将它隐藏在开发人员之外。我宁愿选择马克提出的解决方案。要创建弱引用,可以在块外部执行以下操作之一:

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

#2


1  

I think disabling the warning is currently the only correct way to do, since it says the compiler: Do not care about this retain cycle, I am aware of it and I will dispose of the observer myself. Introducing a weak reference is a costly solution since it comes with runtime CPU and memory overhead.

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

#3


0  

the new LLVM is better at detecting/preventing such retain cycles wait for LLVM shipping with ios6 or do alex's way with creating a weak var.

新的LLVM更好地检测/防止这样的保留周期等待LLVM与ios6一起发货,或者在创建弱变量时采用alex的方式。

disabling the warning is a bad idea though!

禁用警告是一个坏主意!

#4


0  

I wrote the following macro, which I think, is pretty clever...

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

#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))

It allows you to do all sorts of fun things (without Xcode haranguing you), such as..

它允许你做各种有趣的事情(没有Xcode haranguing你),如..

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

You can put in any warning flag and Clang will heed to your whims...

你可以放入任何警告标志,Clang会留意你的想法......

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

Then again, the warnings usually are there for a reason. Party poppers.

然后,警告通常是有原因的。派对poppers。

#5


0  

To solve the problem of the clunkiness of creating a weak reference, I put that into a macro. It uses the preprocessor to create a new var with the same name but with a prefix ('w', in this case; I avoided 'weak' because that would be overkill and mess more with capitalisation rules):

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

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

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

If, otoh, a weak reference is not desirable, don't use it! I like nielsbot solution of passing the object as a parameter (when possible, of course).

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