Running selector with mainThread

Discussion in 'iOS Programming' started by youPhone, Jan 23, 2009.

  1. youPhone macrumors member

    Joined:
    Sep 8, 2008
    #1
    Some things need to be run on the mainThread as per the guidelines. One example is reloadData for a table. If I don't run this on mainThread I will get problems or even crashes. Other things like updating frames have to be done like this too.

    I was wondering if this is a safe way to make sure something is run on the mainThread. I've tested it out some and it always seems to work, but it could be a bad practice.


    Code:
    - (void) reloadTable
    {
    	if ([NSThread isMainThread]) {
    		@synchronized (self.tableView) {
    			[self.tableView reloadData];
    		}
    	} else {
    		[self performSelectorOnMainThread:@selector(reloadTable) withObject:nil waitUntilDone:YES];
    	}
    }
    
    The only problem I see is if somehow the performSelectorOnMainThread somehow didn't get called on the mainThread and it got stuck in an infinite loop
     
  2. newb16 macrumors regular

    Joined:
    Feb 27, 2008
    #2

    I don't know if it is different from 'big' os x, where it posts an event in event loop and waits until it is processed. So it will be fetched sometime and will be processed. Btw, isn't @synchronised within if(is_main_thread) redundant?
     
  3. jnic macrumors 6502a

    Joined:
    Oct 24, 2008
    Location:
    Cambridge
    #3
    Is the if/else not rather redundant given that performSelectorOnMainThread will have the same effect regardless of which thread you're in (including the main thread)?
     
  4. youPhone thread starter macrumors member

    Joined:
    Sep 8, 2008
    #4
    I have had some weird display problems if I don't run reloadData on the main thread. There are other cases such as setting text in a UILabel where the text will not display unless that command was called on the main thread. So I could have used the example:

    Code:
    - (void) setLabelText:(NSString*)someText
    {
    	if ([NSThread isMainThread]) {
    		someLabel.text = someText;
    	} else {
    		[self performSelectorOnMainThread:@selector(setLabelText:) withObject:someText waitUntilDone:YES];
    	}
    }
    
    So my real question is if this would be good practice or not. I seem to be wanting to use this code in quite a few places where I download XML feed data on a new thread, then when the data is processed, I want to update the display layer on the main thread.
     
  5. jnic macrumors 6502a

    Joined:
    Oct 24, 2008
    Location:
    Cambridge
    #5
    What I mean is, why not simply replace

    Code:
    - (void) setLabelText:(NSString*)someText
    {
    	if ([NSThread isMainThread]) {
    		someLabel.text = someText;
    	} else {
    		[self performSelectorOnMainThread:@selector(setLabelText:) withObject:someText waitUntilDone:YES];
    	}
    }
    with

    Code:
    - (void) setLabelText:(NSString*)someText
    {
    	[self performSelectorOnMainThread:@selector(setLabelText:) withObject:someText waitUntilDone:YES];
    }
    Yes, any UI updates must be in the main thread to have any effect, but I can't see any advantage to testing the thread when performSelectorOnMainThread works correctly regardless of the calling thread.
     
  6. youPhone thread starter macrumors member

    Joined:
    Sep 8, 2008
    #6
    Suppose that there are 4 labels that need to be updated. So I can either:

    Code:
    - (void) updateLabels
    {
    	if ([NSThread isMainThread]) {
    		someLabel1.text = someField1.text;
    		someLabel2.text = someField2.text;
    		someLabel3.text = someField3.text;
    		someLabel4.text = someField4.text;
    	} else {
    		[self performSelectorOnMainThread:@selector(setLabelsText:) withObject:someText waitUntilDone:YES];
    	}
    }
    
    or

    Code:
    - (void) updateLabels
    {
    	[someLabel1 performSelectorOnMainThread:@selector(setText:) withObject:someField1.text waitUntilDone:YES];
    	[someLabel2 performSelectorOnMainThread:@selector(setText:) withObject:someField2.text waitUntilDone:YES];
    	[someLabel3 performSelectorOnMainThread:@selector(setText:) withObject:someField3.text waitUntilDone:YES];
    	[someLabel4 performSelectorOnMainThread:@selector(setText:) withObject:someField4.text waitUntilDone:YES];
    }
    
    It seems like running performSelectorOnMainThread:withObject:waitUntilDone: four times in the second code block would be more expensive than running the first code block.

    I could just run
    Code:
    [someObject performSelectorOnMainThread:@selector(updateLabels) withObject:nil waitUntilDone:YES];
    
    I think the main thing I'm trying to do is make certain that some selectors are always run on the main thread. I suppose the header comment for the selector could just say that this selector must be run on the main thread.
     

Share This Page