General Problem
Until now, I always thought self->_ivar
is equivalent to _ivar
. Today I found out that this is not entirely true.
See, for example the following code snippet:
@interface TestClass : NSObject {
NSString *_testIVar;
}
@end
@implementation TestClass
- (instancetype)init
{
if ((self = [super init])) {
_testIVar = @"Testing Only";
}
return self;
}
- (void)test
{
{
NSInteger self = 42;
NSLog(@"without arrow: %@", _testIVar); /* OK */
NSLog(@"with arrow: %@", self->_testIVar); /* COMPILER ERROR! */
}
}
@end
Even though I hid the original self
with some NSInteger
also named self
, the implicit ivar syntax _testIVar
still finds the "original" self whereas self->_testIVar
obviously does not. In the latter case the compiler correctly complains with
Member reference type 'NSInteger' (aka 'long') is not a pointer
In the first case however, it just works.
The Real-world Problem
This example might seem rather artificial but it's not at all. For example the ExtObjC project (used by ReactiveCocoa ) defines the very handy @weakify(var)
and @strongify(var)
which help against strongly capturing self
(and other objects) in blocks by defining a really handy syntax (no need to write the odd and cumbersome to write __weak typeof(self) weakSelf = self; [...] ^{ __strong typeof(self) strongSelf = weakSelf; [...] }
anymore). For example:
- (void)someMethod
{
@weakify(self);
dispatch_async(self.someQueue, ^{
@strongify(self);
NSLog(@"self @ %p", self);
}
}
Without @weakify
and @strongify
, the block would capture a strong reference to self
. With the @weakify
and @strongify
it doesn't. So the deallocation of self
would not be postponed until the block has been run. The main advantage though is that you don't need to remember to use weakSelf
or strongSelf
instead of self
because the "original" self
is hidden.
That's very handy, the ExtObjC implements @weakify
/ @strongify
by generating something similar like the following with macros:
- (void)someMethod
{
__weak typeof(self) _weakSelf = self;
dispatch_async(self.someQueue, ^{
__strong typeof(self) self = _weakSelf;
NSLog(@"self @ %p", self);
}
}
Fair enough, that's even better because we can just continue to use self
without actually capturing a strong reference to self
. However, as soon as we use the implicit-ivars-of-self-syntax, a strong reference to the "original" self
will still be captured!
- (void)someMethod
{
@weakify(self);
dispatch_async(self.someQueue, ^{
@strongify(self); /* compiler warning: Unused variable self here!!! */
NSLog(@"self->_testIVar: %@", _testIVar);
}
}
Misc
When using ivars in blocks, we're definitely capturing self
. See for example this screenshot:
.
Another fun thing about the screenshot is that the warning messages are
Unused variable 'self'
and in the line below
Capturing 'self' strongly in this block is likely to lead to a retain cycle
That's why I think there are two versions of self
:-)
Question
The actual question here is: What exactly does _testIVar
mean? How does it find the "original" self
pointer?
To clarify (also see my screenshot): As @MartinR pointed out (which is what I think as well), there is some special version of self
which cannot be changed and is only used for implicit-self-ivar-access. Is that documented somewhere? Basically where is defined what the implicit self
refers to? It seems to behave the same way that for example Java does (with this
) but with the difference that this
is a reserved keyword that you cannot override.
The question is also not how to "fix" it, just writing self->_testIVar
will be what I want in the @weakify
/@strongify
example. It's more that I thought by using @weakify
/@strongify
you cannot make the mistake of implicitly strongly capturing self
anymore but that simply does not seem to be the case.
self
isn't a reserved word o_O – Pettis- (instancetype)init
method you doif ((self = [super init])) { ... }
, no? So you assign toself
, therefore it can't be a reserved keyword. – Laingself->_block = ^{ NSLog(@"%@", _block); };
(assuming that_block
is an ivar of block type), there will be a compiler warning "Capturing 'self' strongly in this block is likely to lead to a retain cycle". So it does retainself
and not only the ivar. – Laingself
in a new block (i.e. at the top-level of a method). However thinking about it, that's no different from any other variable. – Carolecarolee_ivar
is equivalent toself->_ivar
whereself
is the implicit first argument that each Objective-C method call has, even if there is a local variable of the same name. I don't have an official reference for that (otherwise I would write an answer :-) , but my first attempts to read the generated assembler code confirm this conjecture. – Distressedself = [super init]
also overwrites self, and in that case it is expected that_ivar = ...
sets the instance variable of the "new self". So this might be a special issue with blocks. – Distressedself
, isn't it? If yes, that's basically a non-accessible separateself
variable. But where's that defined? – Laingself
variable that you define yourself. It always uses the implicitself
variable that the compiler defines for the method. – Chlorous