views:

40

answers:

1

6 times out of 10 my very simple iPhone app is getting a corrupted display on launch or crashes randomly. But it behaves fine in the simulator. The display corruption looks like mis-colored fonts, out of place font text, wrong background colors, etc.

I've found a strange work-around.. when my thread delays by 2 seconds before calling the "done" notification, everything works swimmingly. The thread reads a web page and the "done" notification loads up a PickerView with strings. So what gives? Can I not safely initiate a threaded task from viewDidLoad?

- (void) loadWebPage:(NSString *)urlAddition {
      NSAutoreleasePool *subPool = [[NSAutoreleasePool alloc] init];
      NSString *pageSource;
      NSError *err;
      NSString *urlString = [NSString stringWithFormat:@"http://server/%@",
                             urlAddition];
      pageSource = [NSString stringWithContentsOfURL:[NSURL URLWithString: urlString] 
                                            encoding:NSUTF8StringEncoding 
                                               error:&err];

      [NSThread sleepForTimeInterval:2.0]; // THIS STOPS THE DISPLAY CORRUPTION
      [[NSNotificationCenter defaultCenter] postNotificationName:@"webDoneNotification" 
                                                          object:nil]; 
      [subPool drain];
}

- (void) webDoneNotification: (NSNotification *)pNotification { 
      [mediaArray release];
      mediaArray = [[NSArray arrayWithObjects:
                     [NSString stringWithString:@"new pickerview text"], 
                     nil] retain];

      [mediaPickerView reloadAllComponents];

      [mediaPickerView selectRow:0 
                     inComponent:0 
                        animated:NO];
}


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
   mediaArray = [[NSArray arrayWithObjects:
                 [NSString stringWithString:@"init pickerview text"], 
                 nil] retain];

   if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
    }

    return self;
}

- (void)viewDidLoad {
   [super viewDidLoad];

   myWebThread = [[WebThread alloc] initWithDelegate:self];
   [[NSNotificationCenter defaultCenter] addObserver:self 
                                            selector:@selector(webDoneNotification:) 
                                                name:@"webDoneNotification" 
                                              object:nil]; 
   [myWebThread performSelectorInBackground:@selector(loadWebPage:) withObject:@""];
}

Thanks!

Update: Even a delay of 0.1 seconds is enough to completely fix the problem.

+3  A: 

You're updating the view from a background thread. This is what's causing your problems; UIKit views are not thread-safe. When you need to alter the view, do it in the main thread.

Giao
Oh interesting. I'll give this a try, thanks!
Beth S
Instead of posting a notification from your background thread, do `[self performSelectorOnMainThread:@selector(webDone) withObject:nil waitUntilDone:NO];` This schedules the selector to be called on the next pass through the main thread's run loop.
codewarrior
This worked great! Thank you! This bug has been haunting me for a while now. I need to keep the notification because I need to copy some data from my web thread class over to my MainViewController before the thread finishes execution (and autoreleases my data). But adding the performSelectorOnMainThread call after the notification works great for just updating the UI.
Beth S