h.and.h
h.and.h

Reputation: 776

In Objective-c blocks, do I need to pass weak/strong self to helpers?

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

Answers (1)

skaak
skaak

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

Related Questions