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.
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.
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.
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.
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?
To begin with, there is a simple way to disable warnings for certain lines of code using #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
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..
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_IGNORE(-Warc-performSelector-leaks);
return [self performSelector:someIllBegotSelector withObject:arcFauxPas];
CLANG_POP
Then again, the warnings usually are there for a reason. Party poppers.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With