NSOperationQueue & NSInvocationOperation

1. In your main class’ init method call this:

operationQueue = [[NSOperationQueue alloc]init];
[operationQueue setMaxConcurrentOperationCount:1];

2. Then in your viewWillAppear you can call this:

[self showLoadingIndicators]; //calls a method which presents loading indicators (optional)
[self beginLoadingTwitterData]; // this is the method that fires it all off

3. In your beginLoadingTwitterData you call this:

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(synchronousLoadTwitterData) object:nil];
[operationQueue addOperation:operation]; // creates and adds NSOperation to a queue
[operation release];

4. In this synchronousLoadTwitterData is where you do the heavy lifting:

NSDictionary *dict = [[NSDictionary alloc] initWithDictionary:[TwitterHelper fetchInfoForUsername:[twitterIds objectAtIndex:count]]]
// this calls for a method “fetchInfoForUsername” which returns an NSDictionary, but to do so, it connects to the internet using a synchronous URL fetch which would normally hold up the main thread

Later in this method you call:

[self performSelectorOnMainThread:@selector(didFinishLoadingTwitterDataWithResults:) withObject:tempArray waitUntilDone:YES];
//which returns you to the main thread at said selector…

5. In this final method, didFinishLoadingTwitterDataWithResults, which gets called once the operation finishes, you call:

self.persons = (NSArray*)result;
[self hideLoadingIndicators];
[self.tableView reloadData];
//and you can do other things like, receive the results, hide the indicators and reload a tableview.

This is very useful when downloading data from the web.


Lets say you are not only downloading tweets (text) from the web, but actual chunks of raw data, such as images. You may have a method that returns a UIImage, such as:

//Preferably look for a cached image
id cachedObject = [cachedImages objectForKey:url];
if (cachedObject == nil)
//set loading placeholder in our cache dict
[cachedImages setObject:LoadingPlacedholder forKey:url];
//Create and queue a new image loading op
ImageLoadingOp *operation = [[ImageLoadingOp alloc] initWithImageURL:url target:self action:@selector(didFinishLoadingImageWithResult:)];
[operationQueue addOperation:operation];
[operation release];
} else if(![cachedObject isKindOfClass:[UIImage class]])
//were already loading the image, dont kick off another request
cachedObject = nil;
return cachedObject;

Except notice this time the operation is not an NSInvocationOperation but rather a Class by itself. That class is responsible for downloading the image from the web, processing it, returning it and caching it.

Upon your return you do this:

NSURL *url = [result objectForKey:@”url”];
UIImage *image = [result objectForKey:@”image”];
[cachedImages setObject:image forKey:url];
[self.tableView reloadData];