-1

I had a job interview this morning and was presented with the following code and asked to find the problem:

@interface TTWaitController : UIViewController

@property (strong, nonatomic) UILabel *alert;

@end

@implementation TTWaitController

- (void)viewDidLoad
{
    CGRect frame = CGRectMake(20, 200, 200, 20);
    self.alert = [[UILabel alloc] initWithFrame:frame];
    self.alert.text = @"Please wait 10 seconds...";
    self.alert.textColor = [UIColor whiteColor];
    [self.view addSubview:self.alert];

    NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];
    [waitQueue addOperationWithBlock:^{
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
        self.alert.text = @"Thanks!";
    }];
}

This would initiate UI updates off of main thread, which is a no-no.

My question is, why, specifically? What types of issues could one expect when updating UI away from the main thread?

Tommy
  • 99,986
  • 12
  • 185
  • 204
Adam North
  • 49
  • 5
  • The main issue is that basically it won't work :) It may happen a range of things but you can basically expect to do nothing in the best case – Claudio Redi Jun 02 '15 at 20:51
  • ..., and crash in the medium-bad case, and behave unpredictably in the worst case. – ipmcc Jun 02 '15 at 21:24
  • UI stuff should always run on the main thread... – Michael Jun 02 '15 at 21:38
  • There are a whole bunch of additional problems, should you have wanted extra credit; failure to call `super` and the questionable lifetime of `waitQueue` being the functionality ones I can spot. The use of integer constants is also questionable stylistically. – Tommy Jun 03 '15 at 06:00

1 Answers1

4

My question is, why, specifically?

Because Apple specifically tells us, on the UIView reference page, that:

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.

There's a similar warning in the Core Image Programming Guide:

CIContext and CIImage objects are immutable, which means each can be shared safely among threads. Multiple threads can use the same GPU or CPU CIContext object to render CIImage objects. However, this is not the case for CIFilter objects, which are mutable. A CIFilter object cannot be shared safely among threads. If your app is multithreaded, each thread must create its own CIFilter objects. Otherwise, your app could behave unexpectedly.

And there's a further clue in Technical Q&A QA1238, which relates to MacOS X but probably applies to iOS as well:

Quartz is thread safe on the whole, but individual Quartz objects are not. In general, you can operate on any object on any thread as long as you guarantee that no two threads are operating on the same object simultaneously. The easiest way to achieve this is to not share your objects between threads.

So it certainly seems that UIView is not thread safe. A view is a mutable object that the main thread needs access to, so trying to modify a view from a different thread is bound to cause problems.

What types of issues could one expect when updating UI away from the main thread?

Imagine that you're working with some object and the data stored in the object changed at random times, all by itself and with no warning. That's what happens when multiple threads all use a mutable object without taking steps to synchronize their access to the object. That can lead to all kinds of problems, from outright crashes to intermittent and hard to find bugs.

Setting the text of a label seems pretty innocuous, right? It's just data: it's pretty unlikely that two threads are going to try to set the label's text at exactly the same time, and even if they do it seems like the worst that might happen is that the label could contain some garbled string. But it's worse than that -- first, the text property takes a NSString*, i.e. a pointer, and if the pointer gets garbled it could easily lead to a memory exception, crashing the app. Also, whenever you set a label's text, the label schedules itself for redrawing. That could put the run loop or the drawing system into an inconsistent state.

The list of bad things that could happen if you ignore the advice about limiting updates to the main thread is impossible to enumerate completely, and probably changes from one iOS release to the next. But ultimately, we really don't need the list -- we just need to know how to avoid the problem.

Limit UI updates to the main thread.

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • 1
    Also: making things thread safe is difficult, and has major performance implications. You want your UI frameworks to be as fast (i.e. responsive) as possible. If you declare that all your UI objects are main-thread only, then you don't have to slow them down with a bunch of synchronization overhead. I've heard it asked, 'if UIKit is really main-thread only, then why don't they just check the current thread and `abort()` if it's not the main thread?' and the answer is the same: Even a simple check like that would result in an appreciable performance hit. – ipmcc Jun 03 '15 at 23:10