Kyle
Kyle

Reputation: 17677

Retaining self with nested blocks?

With the following code:

@interface MyClass()
{
   NSMutableArray * dataArray;
}
@end

@implementation MyClass

- (void) doSomething
{
    __typeof__(self) __weak wself = self;
    dispatch_async(dispatch_get_global_queue(0,0), ^{
       __typeof__(self) sself = wself;
       [sself->dataArray addObject: @"Hello World"];

       dispatch_async(dispatch_get_main_queue(), ^{
         [NSThread sleepForTimeInterval: 30];

         UIAlertView *alert = [[UIAlertView alloc] initWithTitle: sself->dataArray[0] 
                                                         message: @"Message"
                                                        delegate: nil
                                               cancelButtonTitle: @"OK"
                                               otherButtonTitles: nil];
         [alert show];
       });
    });
}

@end
  1. Is this the proper way to access sself from within the main queue block?
  2. Would sself go out of scope once the initial queue finished?
  3. Should I add a second __typeof__(self) sself = wself; inside the main queue block?

Upvotes: 3

Views: 636

Answers (2)

Rob
Rob

Reputation: 437622

You ask:

Is this the proper way to access sself from within the main queue block?

Almost. You also should be checking to make sure sself is not nil, too. You do this because dereferencing a ivar for an nil pointer may crash your app. Thus, check to make sure it's not nil:

- (void) doSomething
{
    typeof(self) __weak wself = self;
    dispatch_async(dispatch_get_global_queue(0,0), ^{
       typeof(self) sself = wself;
       if (sself) {
           [sself->dataArray addObject: @"Hello World"];

           dispatch_async(dispatch_get_main_queue(), ^{
             [NSThread sleepForTimeInterval: 30];

             UIAlertView *alert = [[UIAlertView alloc] initWithTitle: sself->dataArray[0] 
                                                             message: @"Message"
                                                            delegate: nil
                                                   cancelButtonTitle: @"OK"
                                                   otherButtonTitles: nil];
             [alert show];
           });
        }
    });
}

You then ask:

Would sself go out of scope once the initial queue finished?

It goes out of scope, but is retained by the inner block and will be retained until that inner block finishes.

Should I add a second __typeof__(self) sself = wself; inside the main queue block?

You can if you want to, but it's not necessary. It depends upon the desired behavior. If, for example, you want to see this alert even if the current object (presumably a view controller) is dismissed, then use sself pattern (because you presumably want to hang on to it so you have access to dataArray). If you don't want to show the alert anymore, then you'd repeat this weakSelf/strongSelf dance.

Upvotes: 1

Maciej Oczko
Maciej Oczko

Reputation: 1225

  1. Yes, sure it is.
  2. No, you'll be able to use in main_queue block.
  3. No, you don't have to. You don't have to add __typeof__(self) sself = wself; even in global_queue block; it's not necessary, you already have a weakened self object wself (moreover, you will retain self in __typeof__(self) part inside the block).
  4. You don't have to use __typeof__. Use simply typeof(self)

Upvotes: 2

Related Questions