12

I have the following method, which basically calls a request to load an array of NSData of images in the background thread:

[query findObjectsInBackgroundWithBlock:^(NSArray * objects, NSError * error){

}];

In this case objects is an array of the NSData. The issue is that if I have 100 images to load (100 elements in the array). This means that the user will have to wait for quite some time to see any image showing up in a UITableView. What I want to do is for them to see an image once it is available/loaded.. do I have to then change the code so that it does 100 background threads to load the image?

xonegirlz
  • 8,889
  • 19
  • 69
  • 127
  • try to intervene the code and once that Background thread is showing that it has an image, Start Displaying it. Like i video Game loading content in an update Method. – Keeano Sep 21 '11 at 15:10

3 Answers3

48

you could implement something like this in your cellForRowAtIndexPath:

That way you load each image in the background and as soon as its loaded the corresponding cell is updated on the mainThread.

      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
        NSData *data0 = [NSData dataWithContentsOfURL:someURL];
        UIImage *image = [UIImage imageWithData:data0];

        dispatch_sync(dispatch_get_main_queue(), ^(void) {
            UIImageView* imageView = (UIImageView*)[cell viewWithTag:100];
            imageView.image = image;
        });
    });
HeikoG
  • 1,793
  • 1
  • 20
  • 29
  • 1
    Strictly speaking, UIKit (which UIImage is part of) isn't background-thread-safe, but it seems like you're able to get away with it here because you're only using 'UIImage imageWithData'. Nice hack! – Chris Jun 17 '12 at 01:31
  • This is an old answer, but since there are recent comments referencing it, I would like to add this warning: this solution will likely not work correctly in conjunction with cell updates in a table view, and it uses `dataWithContentsOfURL:` for remote access which is not reliable and inappropriate for that purpose. – CouchDeveloper Jul 17 '13 at 21:44
  • Hey. Thanks for your comment. I understand that using this method inside GCD might not be pure elegance but since I never encountered any issues in practice I would be interested in what problems you are seeing in it ? – HeikoG Jul 17 '13 at 22:37
  • For downloading remote resources you are better of using `NSURLConnection`. The methods family "xxxWithContentOfURL:" is really meant to load from file URLs. Then, every serious application will require to cancel a running request. Either this is the user you want enable to _cancel_ a connection when she considers appropriate. And it is you since in your code you often HAVE to cancel a running request when the user switches away from a view and the result isn't used anyway anymore. The latter also prevents accidentally escalation of starting requests, which can hog the CPU and consume memory. – CouchDeveloper Jul 17 '13 at 22:53
  • 1
    Note: when you have to cancel a request, you will need a `NSURLConnection` instance to send it the `cancel` message. This also implies that you _have_ to use `NSURLConnection` in _asynchronous_ mode and implement the delegates. – CouchDeveloper Jul 17 '13 at 22:56
  • I understand your point. However I am using this approach for very small images that serve as thumbnails in a table view. The download process takes a fraction of a second. I think for this purpose it's ok to use this code – HeikoG Oct 04 '13 at 20:41
  • `AFNetworking` had issues with concurrent `-imageWithData:` calls, so it might not be totally safe to call concurrently. https://github.com/AFNetworking/AFNetworking/issues/2572 – Heath Borders May 06 '16 at 20:39
0

You can create NSInvocationOperation and set it to NSOperationQueue

for example: Initializing NSOperationQueue:

NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];

After create NSInvocationOperation:

NSInvocationOperation* downloadOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(selectorToDownloadImage:) object:YouData];    
[operationQueue addOperation:downloadOperation];
[downloadOperation release]; 
Abizern
  • 146,289
  • 39
  • 203
  • 257
Dmytriy
  • 13
  • 3
-1

No, you don't have to create that many background threads. Use NSOperationQueue.

Jim
  • 72,985
  • 14
  • 101
  • 108