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.
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); } }
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
:-)
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.
What are implicit and explicit type conversions in C language? Converting one data type into another data type is called type conversions. The compiler provides implicit type conversions when operands are of different data types.
In the course of initializing an object, it calls setters instead of directly manipulating ivars. As long as those setters are purely synthesized, there is no problem.
The solution is simple: in Objective-C init and dealloc methods, access instance variables directly instead of going through properties. In non-ARC code, check your property attributes for “retain” or “assign”. Then code your direct access to match.
All Objective-C methods are called with two hidden arguments (from the "Objective-C Runtime Programming Guide"):
and a method can refer to the receiving object as self
(and to its own selector as _cmd
).
Now _ivar
is equivalent to self->_ivar
where self
is this implicit first function parameter. As long as you don't define a new variable self
in an inner scope, _ivar == self->_ivar
holds true.
If you define a new variable self
in an inner scope then you have
self
,and _ivar
still refers to the "implicit self"! This explains the compiler warnings in your block, which seem to contradict each other:
self
,The following code demonstrates also this:
@interface MyClass : NSObject { NSString *_ivar; } @end @implementation MyClass - (void)test { _ivar = @"foo"; // Set instance variable of receiver { MyClass *self = [MyClass new]; // Redefine self in inner scope self->_ivar = @"bar"; // Set instance variable of redefined self NSLog(@"%@ - %@", self->_ivar, _ivar); // Output: bar - foo } } @end
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