Ted
Ted

Reputation: 3875

UIImages NSURLs and Threads

I am trying to build a nice function to access the network for images, if they are found on the web, I store them in a cache system I made. If the image was already stored on the cache, I return it. The function is called getImageFromCache and returns an image if it is in the cache, else, it would go to the network and fetch.

The code might look like this:

UIImageView* backgroundTiles = [[UIImageView alloc] initWithImage[self getImageFromCache:@"http://www.example.com/1.jpg"]];

Now, I am moving on to using threads because of big latencies due to network traffic. So I want images to show a temp image before I get the result from the web.

What I want to know is how can I keep track of so many images being accessed sequentially, being added to UIImageViews by this function (getImageFromCache).

Something just won't work there:

-(UIImage*)getImageFromCache:(NSString*)forURL{

    __block NSError* error = nil;
    __block NSData *imageData;
    __block UIImage* tmpImage;

    if(forURL==nil) return nil;

    if(![self.imagesCache objectForKey:forURL])
    {
        // Setting a temporary image until we start getting results
        tmpImage = [UIImage imageNamed:@"noimage.png"];

        NSURL *imageURL = [NSURL URLWithString:forURL];
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            imageData = [NSData dataWithContentsOfURL:imageURL options:NSDataReadingUncached error:&error];
            if(imageData)
            {
                NSLog(@"Thread fetching image URL:%@",imageURL);
                dispatch_async(dispatch_get_main_queue(), ^{
                    tmpImage = [UIImage imageWithData:imageData];
                    if(tmpImage)
                    {
                        [imagesCache setObject:tmpImage forKey:forURL];
                    }
                    else
                        // Couldn't build an image of this data, probably bad URL
                        [imagesCache setObject:[UIImage imageNamed:@"imageNotFound.png"] forKey:forURL];
                   });
            }
            else
                // Couldn't build an image of this data, probably bad URL
                [imagesCache setObject:[UIImage imageNamed:@"imageNotFound.png"] forKey:forURL];

        });

    }
    else
        return [imagesCache objectForKey:forURL];

    return tmpImage;
}

Upvotes: 0

Views: 122

Answers (3)

Bonnie
Bonnie

Reputation: 4953

its good your building it from scratch but if you want to save the all the work, there's a drop in Replacement SDWebImage Library with support for remote images coming from the web, and has all the functionality Like Temp Image, Asychronous Loading, Caching etc, you said you need

Upvotes: 1

James Holderness
James Holderness

Reputation: 23001

In your background thread, once the download has completed and you've saved the image to the cache, I'd suggest you post a notification using the NSNotificationCenter to let other parts of your app know that the cache has been updated.

This assumes that whichever part of the app manages the image views has registered its interest in those notification with the addObserverForName method. When it receives such a notification, it can then attempt to retrieve the images from the cache again and update its image views if appropriate.

Depending on the number of image views, you may want to pass through the image url in the notification in some way (e.g. in the userInfo dictionary), and then based on that decide which image views should be refreshed rather than refreshing them all.

I should add that I would also recommend getting rid of the inner dispatch_async call. There's no need for that, although you may need to add synchronisation to your cache object so it can be safely accessed from the main thread as well as the download thread.

Upvotes: 0

matt
matt

Reputation: 534925

This is not a direct answer to your question, but are you aware that there is no need to use GCD to download things asynchronously (on a background thread)? Just use NSURLConnection and its delegate methods. All your code will be on the main thread but the actual connection and downloading will happen in the background.

(And in fact I have written a class, MyDownloader, that takes care of all this for you:

http://www.apeth.com/iOSBook/ch37.html#_http_requests

Scroll down to the part about MyDownloader and its subclass MyImageDownloader, which is doing exactly the sort of thing you need done here. Moreover, note the subsequent code in that chapter showing how to use a notification when a download completes, prompting the table view that need these images to reload the row that contains the image view whose image has just arrived.)

Upvotes: 1

Related Questions