Reputation: 776
Within blocks, do I need to pass some version of self to helper methods?
In this example, publicMethod() is calling _privateHelper using strongSelf.
But, _privateHelper() is calling _secondPrivateHelper() using self. Is this fine or should I pass down strongSelf
to the helpers?
- (void)publicMethod
{
auto __weak weakSelf = self;
dispatch_sync(dispatch_get_main_queue(), ^{
auto __strong strongSelf = weakSelf;
if (!strongSelf) {
return;
}
[strongSelf _privateHelper];
});
}
- (void)_privateHelper
{
[self _secondPrivateHelper]; // <-- what version of `self` is sending the message here? I'm assuming the copied `strongSelf` from above.
}
- (void)_secondPrivateHelper
{
NSLog(@"Hello, World");
}
Upvotes: 0
Views: 297
Reputation: 3018
TLDR
When to use strong
or weak
depends on how you use the block. In your code strong
and weak
does not really matter but I will give an example where it matters and explain how to then use weak
.
I think you are considering the wrong things here. In your example all pointers to self
can (and should) be strong
. You do not need to dance the dance here and you should consider using weak
only when you create retain cycles which I will discuss in a moment.
In your case note that when you are inside the block you want a strong
pointer to self
. If you have a weak
pointer the block becomes superfluous when self
is nil
. To get a strong pointer simply change your block to
... ^{
[self _privateHelper];
});
rather than the way you do it. Now (and in your original code) you have (and should have) a strong
pointer to self
inside both your _privateHelper
and _secondPrivateHelper
.
But this is not the issue.
Retain cycle
Suppose you have an ivar
@property (strong,nonatomic) void ( ^ block )( void );
Only now do you have potential trouble and you probably need to use weak
pointers. The reason being that if somewhere in your code you have
self.block = ^{ [self _privateHelper]; };
the block will retain a strong
pointer to self
. At the same time, self
is keeping, through the ivar, a strong
pointer on the block. Can you see the retain cycle? Neither the block nor self
will ever be released automatically as both are keeping strong
references to the other.
Fixing it
You can change the ivar to weak
in which case you break the cycle. If the block goes out of scope the weak
ivar goes to nil
and there is no retain on self
.
You could also do
self.block = ^ { [weakSelf _privateHelper]; };
inside the block to break the cycle. Now if self
goes out of scope the block no longer has a hold on it and again there is no retain cycle on self
.
Both these fixes are used - a lot of ivars will be weak
in a UI environment to prevent the e.g. label keeping a strong
reference on the vc while the vc also keeps a strong
reference on the label.
When working with blocks you typically use weak
pointers to self
inside the block only if you potentially have a retain cycle. This is often not an issue and in your code also is not an issue as self
does not retain the block. So in general your helper blocks could probably get away with strong
pointers to self
. You only need to dance the dance if somehow self
and the block are creating a retain cycle.
Finally, a third way to fix it is to set the ivar to nil
yourself. So you could have code that looks like this
if ( self.block )
{
self.block (); // Execute block
self.block = nil;
}
This also makes sense sometimes e.g. when you want to retain the block and self
until the block executes. Here you release the ivar manually by setting it to nil
after which it no longer retains self
and the cycle is broken albeit manually.
You could even do this inside the block, e.g. change the block to
self.block = ^ { [self _privateHelper]; self.block = nil; /* release when done */ };
Upvotes: 1