简体   繁体   中英

Detect if a dispatch_async method is still running

I have a loadImages method

- (void)loadImages {
   dispatch_async(dispatch_get_global_queue(0, 0), ^{
       //this method loads images from a url, processes them
       //and then adds them to a property of its view controller 
       //@property (nonatomic, strong) NSMutableArray *storedImages;
   });
}

When a button is clicked a view enters the screen, and all the images that currently exist in _storedImages are displayed

- (void)displayImages {
   for (NSString *link in _storedImages) {
      //displayImages
   }
}

The problem with this setup is, that if the user clicks the button before all the images are loaded, not all the images are presented on the screen.

Hence, I would like to display an SVProgressHUD if the button is clicked, and the loadImages dispatch_async method is still running .

So, how do I keep track of when this dispatch_async is completed? Because if I know this, then I can display an SVProgressHUD until it is completed.

On a side note, if you know how to load/display the images dynamically that info would be helpful too , ie you click the button and then as you see the current images, more images are downloaded and displayed

Thank you from a first time iOS developer!


Ok I found a solution but it is incredibly inefficient , I am sure there's a better way to do this

 1. Keep a boolean property doneLoadingImages which is set to NO 2. After the dispatch method finishes, set it to YES 3. In the display images method, have a while (self.doneLoadingImages == NO) //display progress HUD until all the images a loaded 

Keep in mind that NSMutableArray is not thread-safe. You must ensure that you don't try to access it from two threads at once.

Using a boolean to track whether you're still loading images is fine. Make loadImages look like this:

- (void)loadImages {
    self.doneLoadingImages = NO;

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        while (1) {
            UIImage *image = [self bg_getNextImage];
            if (!image)
                break;
            dispatch_async(dispatch_get_main_queue(), ^{
                [self addImage:image];
            });
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            [self didFinishLoadingImages];
        });

    });
}

So we send ourselves addImage: on the main queue for each image. The addImage: method will only be called on the main thread, so it can safely access storedImages :

- (void)addImage:(UIImage *)image {
    [self.storedImages addObject:image];
    if (storedImagesViewIsVisible) {
        [self updateStoredImagesViewWithImage:image];
    }
}

We send ourselves didFinishLoadingImages when we've loaded all the images. Here, we can update the doneLoadingImages flag, and hide the progress HUD if necessary:

- (void)didFinishLoadingImages {
    self.doneLoadingImages = YES;
    if (storedImagesViewIsVisible) {
        [self hideProgressHUD];
    }
}

Your button action can then check the doneLoadingImages property:

- (IBAction)displayImagesButtonWasTapped:(id)sender {
    if (!storedImagesViewIsVisible) {
        [self showStoredImagesView];
        if (!self.doneLoadingImages) {
            [self showProgressHUD];
        }
    }
}

What I usually do with this kind of problem is basically the following (rough sketch):

- (void)downloadImages:(NSArray*)arrayOfImages{

  if([arrayOfImages count] != 0)
  {
     NSString *urlForImage = [arrayOfImages objectAtIndex:0];
     // Start downloading the image

     // Image has been downloaded
     arrayOfImages = [arrayOfImages removeObjectAtIndex:0];
     // Ok let's get the next ones...
     [self downloadImages:arrayOfImages];
  }
  else
  {
   // Download is complete, use your images..
  }
}

You can pass the number of downloads that failed, or even a delegate that will receive the images after.

You put a "note" and this may help, it allows you to display the images as they come down, I store the URLs in an array (here it is strings but you could do array of NSURL).

for(NSString *urlString in imageURLs)
{
    // Add a downloading image to the array of images
    [storedImages addObject:[UIImage imageNamed:@"downloading.png"]];

    // Make a note of the image location in the array    
    __block int imageLocation = [storedImages count] - 1; 

    // Setup the request    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
    [request setTimeoutInterval: 10.0];
    request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

    [NSURLConnection sendAsynchronousRequest:request
                  queue:[NSOperationQueue currentQueue]
                  completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
                  // Check the result
                  NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                  if (data != nil && error == nil && [httpResponse statusCode] == 200)
                  { 
                      storedImages[imageLocation] = [UIImage imageWithData:data];
                      [self reloadImages];
                  }
                  else
                  {
                      // There was an error
                      recommendedThumbs[imageLocation] = [UIImageimageNamed:@"noimage.png"];
                      [self reloadImages]
                  }
           }];
}

You then need another method which reloads the display. If the images are in a table then [[tableview] reloaddata];

-(void)reloadImages

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