BlackM BlackM - 4 months ago 15
iOS Question

iOS - GCD and __strong reference

I have this code and what I am trying to do is to keep self alive in the block that it will be executed on the main thread. The result is kind of random and sometimes print null.

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.proArray = [[NSMutableArray alloc]init];

GCDVC2* __weak weakSelf = self;

self.postGCDBlock = ^{

GCDVC2* __strong strongSelf2 = weakSelf;

[strongSelf2.proArray removeObject:@"3"];
NSLog(@"%@",strongSelf2.proArray);
[strongSelf2.activityIndicator stopAnimating];
};

self.addObjectsBlock = ^{

GCDVC2* __strong strongSelf = weakSelf;

[strongSelf.proArray addObject:@"2"];
[strongSelf.proArray addObject:@"3"];
[NSThread sleepForTimeInterval:5];

dispatch_async(dispatch_get_main_queue(),strongSelf.postGCDBlock);
};

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), self.addObjectsBlock);

}


This code works fine:

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.proArray = [[NSMutableArray alloc]init];

GCDVC2* __weak weakSelf = self;


//self.postGCDBlock = ;

self.addObjectsBlock = ^{

GCDVC2* __strong strongSelf = weakSelf;

[strongSelf.proArray addObject:@"2"];
[strongSelf.proArray addObject:@"3"];
[NSThread sleepForTimeInterval:5];

GCDVC2* __weak weakSelf2 = strongSelf;

dispatch_async(dispatch_get_main_queue(),^{

GCDVC2* __strong strongSelf = weakSelf2;

[strongSelf.proArray removeObject:@"3"];
NSLog(@"%@",strongSelf.proArray);
[strongSelf.activityIndicator stopAnimating];
});
};

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), self.addObjectsBlock);

}


Is there any way to convert the second piece of code to work with the structure of the first piece of code? I tried a lot of variations but it's always random. Can I somehow assure that the self.postGCDBlock won't have self to nil?

Update:
Property declaration:

typedef void(^CustomBlock)(void);

@interface GCDVC2 ()
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property(nonatomic,strong)NSMutableArray *proArray;
@property (nonatomic, copy) CustomBlock addObjectsBlock;
@property (nonatomic, copy) CustomBlock postGCDBlock;
@end

Answer

I think your issue lies with this line (I cannot reproduce the failure case with this code):

dispatch_async(dispatch_get_main_queue(),strongSelf.postGCDBlock);

At this point in your addObjectsBlock, strongSelf is holding a reference to self, but that ends when you leave the scope of that block. dispatch_async will copy the postGCDBlock, but that block is not holding a strong reference to self.

To get dispatch_async to hold a strong reference to self, you would want to do something like this:

dispatch_async(dispatch_get_main_queue(), ^{
    strongSelf.postGCDBlock();
});

Wrapping strongSelf in the block will cause dispatch_async to retain strongSelf (and thereby self) long enough for it to call postGCDBlock.