jpurjay
jpurjay

Reputation: 21

Updating the UI using "dispatch_async(dispatch_get_main_queue(),^{block})

I wanted to update the UI in the NSURLSession's completion block. The initial implementation didn't update the UI immediately. It updated the UI maybe 20 seconds later. Here is the initial implementation.

  NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 200, 150, 150)];
        label.backgroundColor = [UIColor greenColor];
        label.text = jsonDict[@"object_or_array"];

        dispatch_async(dispatch_get_main_queue(), ^{ 
            [self.view addSubview:label];
        });
    }];

    [task resume];

I moved the location of the label.text = jsonDict[@"object_or_array"] inside the main queue as following.

 dispatch_async(dispatch_get_main_queue(), ^{ 
                label.text = jsonDict[@"object_or_array"]
                [self.view addSubview:label];
            });

Then the UI was updated immediately as expected.

Could anyone tell me why this is?

Upvotes: 2

Views: 5390

Answers (1)

Jeffery Thomas
Jeffery Thomas

Reputation: 42598

As @rmaddy has said:

Threading Considerations

Manipulations to your application’s user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your application. The only time this may not be strictly necessary is when creating the view object itself but all other manipulations should occur on the main thread.

UIView Class Reference

From this quote, I infer the following rules:

  • Instances of classes that inherit from UIView should only have methods called on the main thread (including property getters and setters).
  • Initialization methods (-init*) of UIKit objects can be done in any thread.
  • Any method can be called on instances of UIKit classes not sub-classed from UIView from any thread.

Here is the code I would feel comfortable with:

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 200, 150, 150)]; // OK because -initWithFrame: is initialization
UIColor *color = [UIColor greenColor]; // OK because UIColor is not a subclass of UIView
NSString *text = jsonDict[@"object_or_array"];

dispatch_async(dispatch_get_main_queue(), ^{ 
    label.backgroundColor = color;
    label.text = text;
    [self.view addSubview:label];
});

But really, the safest is to do it all from the main thread:

dispatch_async(dispatch_get_main_queue(), ^{ 
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 200, 150, 150)];
    label.backgroundColor = [UIColor greenColor];
    label.text = jsonDict[@"object_or_array"];
    [self.view addSubview:label];
});

Unless you are running into performance issues, there is no reason not to.

Upvotes: 3

Related Questions