iPhone OS, NSThread, and You

Hello 9MM blog readers! This is the first post of many about the iPhone development happenings here. Look for more in the future!

Many new iPhone developers are coming from backgrounds in higher level languages (web, javascript, actionscript, etc), where you don't have to think about things like garbage collection or concurrent processes. You can get along programming for the iPhone without ever touching a thread, but to optimize performance on almost any application it is a must. Threading can be a little tricky so I have put together a simple example to help break the ice.

Before we get down to code, it’s worth defining what a thread does: A thread is a way to break up your code into pieces that can run concurrently.

Threads are most commonly used on the iPhone to maintain UI responsiveness. For example, if you wanted to load a large file into memory while keeping a UI animation smooth, you'd put the loading in its own thread to keep the processor rendering frames. The same concept would apply for a background downloader or a chat application so you can keep socket processes separate from the UI.

So, time for some code. The example is an image loader that loads multiple images into memory while one image is fading in inside a ViewController:

The header:

@interface ImageLoadingExampleViewController : UIViewController {
	NSMutableArray* imagesToLoad; //Strings that contain filenames
	NSMutableArray* loadedImages; //The loaded UIImages
	CALayer* fadeView; //Where we're putting our alpha'd image
	int imagesLoaded;
	int totalImages;
}
-(void)startFade;
 
//selector we're going to use for our thread entry point
-(void)loadImageAndAddToArray:(NSString*)fileName;
 
//callback
-(void)loadComplete;

The .m:

//we'll load each image a handful of times to make the load take longer
NSMutableArray* loadedImages; //declared outside of scope so it can be shared
@implementation ImageLoadingExampleViewController
 
/*This is the thread selector. Images will be opened here*/
-(void)loadImageAndAddToArray:(NSString*)fileName {
 
    /*You need this for all threads you create or you will leak! */
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
 
    //Load the image
    UIImage* image = [UIImage imageWithData:data];
    [data release];
 
    [loadedImages addObject:image];
 
    //Tell our callback what we've done
    [self performSelectorOnMainThread:@selector(loadComplete) withObject:fileName waitUntilDone:NO];
 
    //remove our pool and free the memory collected by it
    [pool release];
}
 
- (void)loadComplete:(NSString*)file {
    /* Callback for when the image load is completed.
     * Good for working on the image after it's loaded like
     * putting it in the view, etc.
     */ 
    NSLog(@"loaded %@", fileName);
}
 
- (void)loadView {
    ...
    /* Here we set up the background view to fade in. 
     * It's in the source if you'd like to see it */
 
    loadedImages = [[NSMutableArray alloc] init];
 
    //Start loading images (10 times to slow it down)
    //imagesToLoad is a string array of jpg files
 
    for( NSString* fn in imagesToLoad ){
        int i;
        for( i = 0; i < TIMES_TO_LOAD_IMAGE; i++ ){
            //Start our threads -- this class method creates a new NSThread object to execute
            //the selector of our choice.
            [NSThread detachNewThreadSelector:@selector(loadImageAndAddToArray:) toTarget:self withObject:fn];
        }
    }
}

When using NSThread, there are a few things you should keep in mind:

    • In the application's main thread a NSAutoreleasePool is allocated by default(look at main.c in xcode). When you spawn a selector on its own thread, you need to allocate one or you will be leaking memory that is supposed to go into the pool. If you are getting lots of messages in the console about memory leaks, it's likely because you forgot to do this.

    • Shared variables are very common in multithreaded applications. If you need to reference these outside of a given class you can declare them in a separate header or just outside the class declaration.

    • With shared variables comes responsibility -- if you are operating on a shared resource it becomes a critical section, which means that you need to set up safeguards to make sure that the variable doesn’t get modified at the same time by another process. This can and does happen.

So there you have it. I have also checked the complete project into the public svn at: http://code.9mmedia.com/svn/public/iphone/thread-example/. Check it out if you want to build it and see it in action on your iPhone. In addition to my little tutorial here, it's worthwhile to check out Apple's introduction to threading here:

ADC: Threading Programming Guide

Thanks for reading and happy threading!

Comments

9 Responses to “iPhone OS, NSThread, and You”


  1. 1 Raj

    Excellent and very useful tip. I have bookmarked your post at http://www.iphonekicks.com/tipsandtricks/iPhone_OS_NSThread_and_You

  2. 2 Matt

    Great tutorial… Much easier than another one I was trying to follow! Question: When should you use NSOperation instead of NSThread? The other tutorial I was following used NSOperation…

  3. 3 jeff

    Hey Matt, I’m glad it’s helpful!

    In regards to using NSOperation/NSOperationQueue over NSThread…

    NSOperations are a great way to do essentially what I posted above in the form of NSThread. You can actually set up your NSOperations to perform in sequence, or concurrently, or actually limit the number of concurrent operations going at any given time. It’s very powerful and easy to use.

    That said, I had some problems with the phone’s implementation of these classes, especially when in high memory usage situations. For some reason the queue will randomly empty at rather inopportune times. I’ve filed a RADAR bug (bugreporter.apple.com) but haven’t gotten anything back on it yet. So, I would recommend trying to avoid using it for the time being. When it works though I’d recommend using it setting up your own NSThreads as it easily schedules and executes your tasks in a very thread-safe fashion without much effort.

    Hope this helps!
    Jeff

  4. 4 andy

    why does it work even if the variable is not declared outside the implementation?

    -andy

  5. 5 ai

    i love it

  6. 6 emery

    Your svn server doesn’t seem to be up. I’d like to download your thread example project if possible. Thanks!

  7. 7 lauren

    @emery,
    we are in the process of changing over our svn server. all links will be updated within a day or so, please check back soon.

  1. 1 iPhone OS, NSThread, and You at 9MMEDIA Blog | IPHONE NEWS
  2. 2 threading (image loading)+ for loop ?! - iPhone Dev SDK Forum

Leave a Reply