Using weak self in dispatch_async function
Asked Answered
C

2

61

I read a lot of posts about using __weak self inside dispatch_async, and now I am a litle bit confused.

if I have :

self.myQueue = dispatch_queue_create("com.biview.core_data", NULL);

dispatch_async(self.myQueue, ^(void){
    if (!self.var1) {
        self.var1 = ...;
    }
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        if ([self.var2 superview]) {
            [self.var2 removeFromSuperview];
        }

        [self.Label setText:text];
    });
});

do I need to use __weak self. Because I read that in some cases dispatch_async not need a __weak self.

See last comment here

Cookshop answered 24/2, 2014 at 11:55 Comment(0)
S
150

Assuming, self is an object pointer to a UIViewController.

Things to consider:

  • A UIViewController is a "UIKit" object. UIKit objects shall not be sent methods on non-main threads, that is - those methods must execute on the main thread only!

  • A block that has been enqueued in a queue - whether this was synchronously or asynchronously - will eventually be executed -- no matter what! Well, unless the program terminates before this can happen.

  • Captured retainable strong pointers will be retained when the block will be copied (for example, when dispatched asynchronously), and again released when the block will be destroyed (after it finished).

  • Captured retainable weak pointers will NOT be retained and not released.

In your scenario, where you capture self in the block which is dispatched on the main queue, you don't need to worry that bad things happen.

So, why? And what happens actually?

Since self will be captured in the block which is dispatched asynchronously, self will be implicitly retained, and released again when the block has been finished.

That means, the life-time of self will be extended up until after the block finishes. Notice that your second block is dispatched on the main thread, and it's guaranteed that self is still alive when that block gets executed.

This "extended life" above, might be a desired feature of your program.

If you explicitly don't want to extend the life-time of the UIViewController object, and instead want the block - when it finally executes - check whether this UIViewController object does still exist at all, you can use a __weak pointer of self. Note that the block gets eventually executed, no matter whether the UIViewController is still alive or has been deallocated in the mean time.

You might want the block doing "nothing" if the UIViewController has been deallocated before the block will get executed:

MyController* __weak weakSelf = self;
dispatch_async(queue, ^{
    MyController* strongSelf = weakSelf;
    if (strongSelf) {
        ...
    }
    else {
       // self has been deallocated in the meantime.
    }
});

See also: Transitioning to ARC Release Notes

Remember: UIKit objects shall not be sent methods on non-main threads!

One other subtle error may occur due to the fact that UIKit objects shall execute methods only on the main thread.

This can be violated, if a block captures a UIKit object which is dispatched asynchronously, and executes on a non-main thread. It then may happen that the block holds the last strong reference to that UIKit object. Now, when the block gets eventually executed, the block will be destroyed and the UIKit object will be released. Since this is the last strong reference to the UIKit object, it will be deallocated. However, this happens on the thread where the block has been executed - and this is not the main thread! Now, bad things can (and will usually) happen, since the dealloc method is still a method sent to a UIKit object.

You can avoid this error, by dispatching a block capturing a strong pointer to that UIKit object, and send it a dummy method:

UIViewController* strongUIKitPointer = ... 
dispatch_async(non_main_queue, ^{
    ... // do something 
    dispatch(dispatch_get_main_queue(), ^{
        [strongUIKitPointer self];  // note: self is a method, too - doing nothing
    });
});

In your scenario though, the last strong reference could be only in the block which executes on the main thread. So, you are safe from this subtle error. ;)

Edit:

In your setup, you never have a retain cycle. A retain cycle occurs if a retainable object A strongly references another retainable object B, and object B strongly references A. Note that a "Block" is also a retainable object.

A contrived example with a cyclic reference:

typedef void(^my_completion_block_t)(NSArray* result);

@interface UsersViewController : UIViewController
@property (nonatomic, copy) my_completion_block_t completion;
@property (nonatomic) NSArray* users;
@end

Here, we have a property completion whose value type is a Block. That is, we get an ivar with name _completion whose type is a Block.

A client may set a completion handler which should be called when a certain operation has finished. Suppose, the operation fetches a list of Users from a remote server. The plan is to set the property users once the operation finished:

The careless approach would accidentally introduce a cyclic reference:

Somewhere in "UsersViewController.m"

self.completion = ^(NSArray* users){
    self.users = users;
}

[self fetchUsers];  // start asynchronous task

Here, self holds a strong reference to the ivar _completion, which is a block. And the block itself captures self, which causes to retain self when the block gets copied when it is dispatched. This is a classic reference cycle.

In order to avoid that cyclic reference, we have a few alternatives:

  1. Using a __weak qualified pointer of self

    UsersViewController* __weak weakSelf = self;
    self.completion = ^(NSArray* users) {
        UsersViewController* strongSelf = weakSelf;
        if (strongSelf) {
            strongSelf.users = users;
        }
        else {
            // the view controller does not exist anymore
        }
    }   
    [usersViewController fetchUsers];
    
  2. Using a __block qualified pointer of self and eventually setting it nil in the block when it finishes:

    UsersViewController* __block blockSelf = self;
    self.completion = ^(NSArray* users) {
        blockSelf.users = users;
        blockSelf = nil;
    }   
    [usersViewController fetchUsers];
    

See also: Transitioning to ARC Release Notes

Subspecies answered 24/2, 2014 at 12:53 Comment(14)
wow, this was very informative answer, thanks. So if I understand correctly, if after dispach_async calling at least one function on the main thread, even if it is a dummy one it prevent to get retain cycle and I don't need __weak reference to self?Cookshop
@MarkoZadravec In your setup, you never have a retain cycle. A retain cycle occurs if a retainable object A strongly references another retainable object B, and object B strongly references A. Note that a "Block" is also a retainable object. You will get a retain cycle if your ViewController has a strong block ivar, and that block will capture self: ViewController owns block, block owns ViewController. The latter is what you need to avoid.Subspecies
Can you please give me a example of this. I think I understand, but I want to be shore.Cookshop
@MarkoZadravec OK, I added an example which causes a retain cycle and solutions to prevent this.Subspecies
Wow that was great. So If for example I use onelly ‘dispatch_async(self.coreDataQueue, ^(void){...‘ it can't get into retain cycle because this block doesn't have strong iVars to retain ‘self‘.Cookshop
@MarkoZadravec Yes, at least no retain cycle involving self. Since self won't have a reference to the block, the block can capture self -- without introducing a retain cycle. Nonetheless, be careful: retain cycles can occur in more subtle and non-obvious scenarios.Subspecies
@Subspecies nice info about sending the dealloc message to an UIKit object in non-main thread. Didn't know about that. Cheers.Drunk
What in case of declaring the block in the scope of the method like my_completion_block_t completion = ^(...){}? In that case you shouldn't use a weak reference to self (same as dispatch_ blocks), right?Indolence
@Priya There are certainly several ways to accomplish this. This is however not related to this answer or the OP's question. I would suggest to ask this as a separate question on SO. See also: stackoverflow.com/help/how-to-askSubspecies
"Use UIKit classes only from your app’s main thread or main dispatch queue, unless otherwise indicated. This restriction particularly applies to classes derived from UIResponder or that involve manipulating your app’s user interface in any way." - source, so it is not problematic if a view controller is called from a non-main thread and doesn't manipulate the UIObsess
@ɯɐɹʞ You would not use a View Controller for functions not manipulating the UI. On the other hand, if you have an array of items as instance variable in a VC, and use that to render cells in a Table View, this array is already "manipulating" the UI - and it would be a data race to access the array from non-main threads - unless you synchronise the array and items explicitly.Subspecies
@CouchDeveloper, why would Apple put that disclaimer then?Obsess
@ɯɐɹʞ Well, it seems to be a fact, that there is "Thread confinement on the main thread" for UIKit functions, which means, any API in UIKit should be called only from the main thread. There are a few exceptions, though. These exceptions are mostly graphic related, which presumable use synchronisation primitives internally. You should in fact not even just read seemingly simple properties from a non-main thread which just return some state as an integral value (Int), because actually this has been modified from some UI related function internally on the main thread. This is a data race.Subspecies
@CouchDeveloper, could we discuss this further in chat?Obsess
B
31

Swift update:

An example of this so-called strong-weak dance in swift:

Swift 4.2:

func doSomeThingAsynchronously() {
    DispatchQueue.global().async {
        // Do task in default queue
        DispatchQueue.main.async { [weak self] in
            // Do task in main queue
            guard let self = self else { return }
            self.updateView()
        }
    }
}

Swift 3 & 4:

func doSomeThingAsynchronously() {
    DispatchQueue.global().async {
        // Do task in default queue
        DispatchQueue.main.async { [weak self] in
            // Do task in main queue
            guard let strongSelf = self else { return }
            strongSelf.updateView()
        }
    }
}

Swift 2:

func doSomeThingAsynchronously() {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> () in
        // Do task in default queue
        dispatch_async(dispatch_get_main_queue(), { [weak self] () -> () in
            guard let strongSelf = self else { return }
            // Do task in main queue
            strongSelf.updateView()
        })
    }
}

Popular open source project Alamofire uses this approach.

Extend object lifetime using the [weak self] and guard let strongSelf = self else { return } idiom.

For more info check out swift-style-guide

Bismuthinite answered 4/5, 2016 at 11:10 Comment(2)
Fun fact: you can avoid the strongSelfsyntax in swift with guard let `self` = self else { return }Sepal
The use of self is actually a compiler bug and may be removed in future releases of Swift. See the full explanation from Swift's Chris Lattner, here: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/…Scholem

© 2022 - 2024 — McMap. All rights reserved.