简体   繁体   中英

iPhone - GCD doesn't work for the first time

In my app, I load image from the URL:

-(void)loadImages
{
    ...

    image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:imgUrl1]];
}

In order to avoid blocking the main thread until the download has completed I call this method in -viewDidAppear using GCD :

dispatch_async( dispatch_get_global_queue(0,0), ^{
            [self loadImages];
        });

However, when I open my view controller with the imageView at the first time, the imageView is empty (even if I wait for a long time) but after I open this view controller again and image appears and everything is good to go.

Where is my mistake ? Sorry, new to multithreading :)

EDIT : I also forgot to mention, that I use the image in the tableView when I get it:

 cell.imageView.image = image1;

UIElements in iOS should always be updated via main thread. What you could do is:-

   __block NSData *data;
    dispatch_queue_t myQueue = dispatch_queue_create("com.appName", NULL);
    dispatch_async(myQueue, ^{
       data = [NSData dataWithContentsOfURL:imgUrl1];
       dispatch_async(dispatch_get_main_queue(), ^(void) {
       cell.imageView.image = [UIImage imageWithData = data];
    });
    });

or Else you there is a better way of fetching images from URL by using AFNetworking. It is faster and easier. You just have to write one line of code:-

[cell.imageView setImageWithURL:imgUrl1];

This may not be the answer that you were looking for, but that's not the recommended way to load URLs. You should use the URL loading classes available such as NSURLRequest and NSURLConnection.

Try this:

NSURLRequest *imageRequest = [[NSURLRequest alloc] initWithURL:imageURL];
[NSURLConnection sendAsynchronousRequest:imageRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    if (!error) {
        UIImage *image = [[UIImage alloc] initWithData:data];
        [imageView setImage:image];
    }
}];

You need to inform the view that the image needs to be redrawn. Add:

[imageView setNeedsDisplay];

to the end of your loadImages method.

You have many problems here:

  1. You can not start networking requests on an thread not running a runloop.
  2. You can not update your UI from a thread other than the main thread.
  3. [NSData dataWithContentsOfURL:imgUrl1] is not a safe way to load an external resource, even on a different thread (but especially on the main thread).
  4. Any time you dispatch to a different thread, you run the risk that your table cell has been recycled and is no longer showing the data you think it is. (It's still the same cell instance, but is now showing some other row's data.)

What you should be doing:

  1. Start your network operation using asynchronous calls on the main thread. (You can use another thread or queue if you want, but you need to make sure it's running a runloop.)
  2. From your delegate messages, dispatch your image decoding on a different thread.
  3. After the image is decoded, dispatch back to the main thread to update it.
  4. Before actually assigning the image, check that the cell is still being used for the purpose you think.

You can solve the first three problems by using AFNetworking . It wraps the delegate methods and lets you just provide a success and failure block. AFNetworking's AFImageRequestOperation in particular bounces code between queues as I've described. (It even runs its main networking loop in a different thread, which isn't necessary but since it does it well, why not?)

You'll still need to verify the cell's identity.

Since you are using it in TableView, add [self.tableView reloadData]; in the end of loadImages method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM