(Multi)Threading the Needle

by Louis Tur


(originally posted on Medium)

As someone who is habitually running late, multi-tasking is something that I look to as a fine art in reducing the overhead I experience in life. How to fit 45 minutes of things into a 10 minute span has become an art form born of rigorous trial-and-error and critical necessity.But realistically, I’m not multi-tasking in a purest sense.. I can’t. Humans are just physically limited by only having a single brain. And so, we can do a number of tasks in a serial manner and can switch between them very quickly. However, it tends to not be very efficient in most cases, even causing time bottlenecks.

But, computers can truly multitask through use of threads and queues in a process called multithreading. It’s analogous to a human having two or more brains and one brain is coming up with prime numbers up to 1000 while the other one paints a picture: neither brain interferes with the other and the tasks are carried out autonomously.

In this post, I’ll be talking about multi-threading in the context of retrieving data from an internet source.. in this case an API that queries Imgur for random pictures of pugs (I will definitely be updating this so that it locates cat pictures).

Like many great projects, this one has a humble beginning:

  1. Create a new Xcode Project
  2. Start off with a single-view application then delete the view and replace it with a UITableViewController
  3. Create a single prototype cell that has a UIImageView as its only content



Our project is going to have a single Pod dependency of AFNetworking to perform our fetch requests, so be sure to include that in your workspace. The beauty of AFNetworking is that it handles much of the multi-threading on its own. You just have to make sure you’re handling the responses correctly. That said, let’s get some basic configurations out of the way for our expected properties and required/optional UITableView Delegate/ DataSource methods. Because our goal is to query a web service for a list of images, we’ll likely need to store our results in an NSArray to later place on the UITableView cells. Not only that, but maybe we’d like to preserve the link URLs for each image

@property (strong, nonatomic) NSMutableArray * bombAssPugBomb; // URL strings
@property (strong, nonatomic) __block NSMutableArray * pugImages; // Images
// we use a block macro to be able to use this array in a block



For this example, I’ve gone and created a custom UITableViewCell class called FISPugCell to further customize my table. Its implementation is very straightforward and doesn’t do much at all besides set up a UIImageView that takes up the entire cell with a height of 300pt. With the aforementioned in place, we can predict and fill in what our cells will actually look like/contain, thus the cellForRowAtIndexPath: method will look something like this:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

//1
FISPugCell * pugCell = (FISPugCell *)[tableView dequeueReusableCellWithIdentifier:@"pugCell"];  
//2
[pugCell.pugImageView setContentMode:UIViewContentModeScaleAspectFit];
//3
[pugCell.pugImageView setImage:self.pugImages[indexPath.section]];

return pugCell;  
  1. We create our subclasses UITableViewCell using the dequeueReusableCellWithIdentifier: method
  2. We change the content mode so that the cell will accommodate the image regardless of dimension
  3. Lastly, we set the cell’s image to pugImages at the indexPath.section (we use section here instead of the usual row because each image will have it’s own section. So in this scenario, row will always be 1 but the section will reflect the index of the image.



In this implementation, we’re going to toss everything we need into a single method in the main view controller. Eventually, we’ll want to refactor these pug requests into a separate class, but for now, let’s get it working.

The Pugs
-(void) makingAPugRequest:void(^)(UIImage *))imageBlock

Is the line that launched a hundred pugs

AFNetworking handles a lot of the threading issues, but we’re going to do some managing of our own so that we can ensure that the UI will remain responsive even when we make a bulk request to pull in 100 pug images at once. With this in mind, we’re going to create an NSOperationQueue specifically to perform our network calls. Moreover, we want to allow this queue to perform a good deal of pug requests at once, so we’re going to set its max operation count to 10 (basically means that it will be allowed to perform up to 10 tasks at the same time on this queue).

NSOperationQueue * pugRushQueue = [[NSOperationQueue alloc] init];  
[pugRushQueue setMaxConcurrentOperationCount:10];

With the queue created (by default, XCode will make this a background thread), we’ll need to set up our API requests. Since we’ll be deciding which operation queue requests will be placed in, we’ll be using the AFHTTPRequestOperationManager and AFHTTPRequestOperation classes.

//url to make the request
  static NSString *const kPugBaseURL = @"https://pugme.herokuapp.com/random";


/* .... more code .... */


  AFHTTPRequestOperationManager * pugBombManager = [[AFHTTPRequestOperationManager alloc] init]; //operation manager instance
  NSURLRequest * hundredPugRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:kPugBaseURL]];

  // method returns AFHTTPRequestOperation
  AFHTTPRequestOperation * pugRequestOperation = 
  [pugBombManager HTTPRequestOperationWithRequest:hundredPugRequest
                                        success:^(AFHTTPRequestOperation *operation, id responseObject){ ... }
                                        failure:^(AFHTTPRequestOperation *operation, NSError *error)   { ... }
                                        ];

  //url to make the request
  static NSString *const kPugBaseURL = @"https://pugme.herokuapp.com/random";


/* .... more code .... */


  AFHTTPRequestOperationManager * pugBombManager = [[AFHTTPRequestOperationManager alloc] init]; //operation manager instance
  NSURLRequest * hundredPugRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:kPugBaseURL]];

  // method returns AFHTTPRequestOperation
  AFHTTPRequestOperation * pugRequestOperation = 
  [pugBombManager HTTPRequestOperationWithRequest:hundredPugRequest
                                        success:^(AFHTTPRequestOperation *operation, id responseObject){ ... }
                                        failure:^(AFHTTPRequestOperation *operation, NSError *error)   { ... }
                                        ];

/* .... more code .... */


    [pugRushQueue addOperation:pugRequestOperation];

Lastly, we begin the actual request by passing the returned AFHTTPRequestOperation into the addOperation: method of our previously defined NSOperationQueue

But are there pugs yet...?

Now that we’ve established the first HTTP request, let’s fill out the success block (since we’ll just assume the URL we’re hitting is correct). But, we're not exactly sure what we're going to get back from this request, so let’s check to see what our request returns:

NSLog(@"You want a response? %@", responseObject); // 1  
NSURL * pugURL = [NSURL URLWithString:responseObject[@"pug"]];  
[self.bombAssPugBomb addObject:responseObject[@"pug"]]; //2
NSURLRequest * pugImageRequest = [NSURLRequest requestWithURL:pugURL];  
  1. Running the operation at this point will yield a responseObject with a dictionary key of “pug” which will have a URL string to use to get the actual pug image URL (yes, you make a request to this API URL to be issued a random pug image URL). Using this URL, we start another request using the randomized pug URL
  2. Now we’re going to repeat ourselves a little by way of creating a new AFHTTPRequestOperation that will be nested inside of the first call:
AFHTTPRequestOperation * pugImageOperation = [[AFHTTPRequestOperation alloc] initWithRequest:pugImageRequest];  
AFImageResponseSerializer * pugImageSerializer = [AFImageResponseSerializer serializer];  
pugImageOperation.responseSerializer = pugImageSerializer;  

The major difference in this call is that we’re setting the responseSerializer of the AFHTTPRequestOperation to be an instances of a AFImageResponseSerializer. Why do this? Well, AFImageResponseSerializer is already defined to handle an incoming data stream of an image. Since we anticipate an image, we use this type of serializer so that we can manipulate the image, or call image-specific methods on our responseObject. If you’re wondering whether we can simply convert the responseObject (type id) to a UIImage, the answer is yes… but it involves some conversion from NSData type into UIImage. Trust AFnetworking on this one, the image serializer is a better option.

...let's call the pugs, dammit
[pugImageOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) 
    {
        UIImage * pugImage = (UIImage *)responseObject; 
        // 1
        [[NSOperationQueue mainQueue] addOperationWithBlock:^
        {
            imageBlock(pugImage);
            [self.tableView reloadData];
        }];
    }
            failure:^(AFHTTPRequestOperation *operation, NSError *error)
    {
        NSLog(@"The error: %@", error);
    }];
        //2
[pugRushQueue addOperation:pugImageOperation];

OK, let’s talk blocks.. we've gone waaay deep into an Inception-esque block nest that’s going to need some maneuvering out. 1) In the success block, we assume we get the pug image back from the requested URL, and so we pass this information to the block variable from the first AFHTTPRequestOperation. Not only that, but the action is shoved onto the application’s mainQueue, ensuring that the results will get update the UI immediately after the request finishes! So… flow… this is kind of hard, so stay with me:

It’s a bit much to wrap your head around thanks to the blocks, but consider this analogy: In your backyard, you have a well that is filled from an underground spring. But, the source is far below the surface, so you must lower down a bucket to collect some of the water before you can make any use of it. Once your bucket is filled, and you retrieve it, you dip a glass in the bucket to get a smaller, desired, amount of water. Follow?

Each container that the water fills is a block: they each perform a certain function (collecting water), but can only return their contents if there is something available to receive them. By nesting the items, you can achieve the desired results of filling your glass to drink water. Once your glass was filled, then we can drink the water.

Easy, right?

Be sure to check out the full code on Github: MultiPug Bomb

Louis Tur

"How" has been the single most used word in my literary arsenal for as long as I can remember. I've never really been satisfied knowing that something works, but only by knowing how it works.

Read more from this author