Enzo
Enzo

Reputation: 944

setting property in dispatch_async but property is NULL after block finishes

I am using the following code to change a property called topPlaces inside a view controller. The line [FlickrFetcher topPlaces] returns an NSArray and my property topPlaces is, of course, also an NSArray.

dispatch_queue_t downloadQueue = dispatch_queue_create("flickr topPlace", NULL);
dispatch_async(downloadQueue, ^{
    NSArray *topPlaces = [FlickrFetcher topPlaces];
    dispatch_async(dispatch_get_main_queue(), ^{
        self.topPlaces = topPlaces;
    });
});
dispatch_release(downloadQueue);

However, after the block finishes executing, if I log the value of self.topPlaces, that is still NULL for some reason. Is there anything I am missing?

Upvotes: 0

Views: 1000

Answers (2)

BergQuester
BergQuester

Reputation: 6187

Your ivar will not be set until after your current method has finished. Your call to [FlickrFetcher topPlaces] is running in parallel to your current method and takes a random amount of time to complete. When it is complete, it makes a call back to the main thread, which is executed on the next iteration of the run loop

This means that in your second dispatch_async() block you need to call any methods to display the data after setting the ivar.

Upvotes: 3

Jeremy Fox
Jeremy Fox

Reputation: 2668

Please first try to stub self.topPlaces like this:

dispatch_queue_t downloadQueue = dispatch_queue_create("flickr topPlace", NULL);
dispatch_async(downloadQueue, ^{
    NSArray *topPlaces = [FlickrFetcher topPlaces];
    dispatch_async(dispatch_get_main_queue(), ^{
        self.topPlaces = @[@"test", @"test2", @"test3"];
    });
});

Then check the value of self.topPlaces. If it is still NULL then I would need to ask what lifetime qualifier does your property self.topPlaces have (Ex. strong, weak, assign)? If it is weak then of course the value of topPlaces will be NULL after assigning it because there will not be any strong pointers to it. If it is strong then the value of NSArray *topPlaces = [FlickrFetcher topPlaces]; is NULL when execution reaches self.topPlaces = topPlaces;.

The other thing to consider is that when you perform asynchronous actions, execution on the main thread will continue to execute. So, if you are doing the following...

dispatch_queue_t downloadQueue = dispatch_queue_create("flickr topPlace", NULL);
dispatch_async(downloadQueue, ^{
    NSArray *topPlaces = [FlickrFetcher topPlaces];
    dispatch_async(dispatch_get_main_queue(), ^{
        self.topPlaces = topPlaces;
    });
});
NSLog(@"topPlaces = %@", self.topPlaces);

Then I would expect self.topPlaces to always be NULL when it hits the NSLog due to the fact that it wont be set until after [FlickrFetcher topPlaces] has finished and returned and execution continues into the dispatch_async(dispatch_get_main_queue().... At that point the value should be set. You may want to do something like the following to ensure you are not only setting the property but perform some sort of update action to update your UI after your asynchronous actions have completed...

dispatch_queue_t downloadQueue = dispatch_queue_create("flickr topPlace", NULL);
dispatch_async(downloadQueue, ^{
    NSArray *topPlaces = [FlickrFetcher topPlaces];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self updateUIWithTopPlaces:topPlaces];
    });
});

- (void)updateUIWithTopPlaces:(NSArray*)topPlaces {
    self.topPlaces = topPlaces;
    // Perform your UI updates here
}

Upvotes: 2

Related Questions