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.