Reputation: 6205
I'm wondering is the variable declaration from the question topic is legitimate. Imagine the following code:
__weak typeof(self) weakSelf = self;
[self doSomethingThatMayCauseRetainCycleWithBlock:^{
typeof(self) self = weakSelf; // <---- !!!!
if (self == nil) return;
NSAssert(self.someProperty != nil, @"This doesn't lead to retain cycle!");
[self doSomething];
self.someProperty = someValue;
// even
self->someIvar = anotherValue;
}
This code works perfectly in Xcode 4.5.2, only giving a warning that Declaration shadows a local variable
.
What's the point of this quirk:
self
as a strong reference to a weak variable, you can safely copy/move code inside/outside the block without a risk to occasionally create a retain cycle (except for ivars, but they are evil).NSAssert
in a block doesn't cause retain cycle anymore.Update
I discovered that this technique is used in libextobjc
for @weakify/@strongify
macros.
Upvotes: 4
Views: 2993
Reputation: 44876
There's two parts to this answer:
GCC_WARN_SHADOW
, and why it's probably a bad warning to have turned on.NSAssert
that doesn't need this trickeryThe warning you're getting is from GCC_WARN_SHADOW
, one of the more useless warnings (in my opinion, anyway). The compiler is doing the right thing here, but thanks to GCC_WARN_SHADOW
it's drawing your attention to you possibly doing something other than you intended.
This is the sort of things the more paranoid compiler warnings are good at. The downside is that it's difficult (not impossible) to drop a particular warning to indicate that you know what you're doing.
With GCC_WARN_SHADOW
on, this code will generate a warning:
int value = MAX(1,MAX(2,3));
Despite that, it will function perfectly.
The reason for this is that it compiles down to this, which is ugly but (to the compiler) perfectly clear:
({
int __a = (1);
int __b = (({
int __a = (2);
int __b = (3);
__a < __b ? __b : __a;
}));
__a < __b ? __b : __a;
})
So what you're proposing will work well. NSAssert
is implemented using a macro which uses the self
variable. If you define self
in your scope, it'll pick up that self
instead.
But that said, if you think GCC_WARN_SHADOW
has a use, there's another solution. It might be better anyway: Provide your own macro.
#define BlockAssert(condition, desc, ...) \
do { \
__PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \
if (!(condition)) { \
[[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \
object:strongSelf file:[NSString stringWithUTF8String:__FILE__] \
lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \
} \
__PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \
} while(0)
This is essentially a copy-paste of NSAssert
, but it uses strongSelf
instead of self
. In places you use the strongSelf
pattern, it'll work; in places where strongSelf
is not defined, you'll get a compiler error: Use of undeclared identifier: 'strongSelf'. I think that's a pretty good hint.
Upvotes: 5
Reputation: 12421
Strictly speaking, there is nothing wrong with your code — the variable declaration is legitimate. However, you will probably get a compiler warning about the local variable shadowing the instance one.
GCD blocks are actually C functions, not Objective-C methods. When compiled, every Objective-C instance method has an extra parameter added to it, which is the self
pointer. self
isn't stored in the object struct like other variables.
For this reason, I would hesitate to use this code in a library I was going to share. The code may break with newer versions of then compiler because you're actually hacking the runtime a little more than is immediately apparent. Additionally, it's quirky code, as you point out :) I'm not sure that anyone else reading it would immediately understand what's going on.
Upvotes: 3