0

I've a uitableview which shows images in each cell, which are downloaded online.

To make this call async, I use NSBlockoperation. I prefer to use this, because i used GCD before but you cannot cancel GCD. The reason is that if I leave the view, the images gets downloaded at the background of the App, and when I get into the previous view again GCD would let it queue all again, so eventually there would be a whole stack of images and the user would never see the uitableview. So thats why I choose for NSBlockoperation.

However, my blocks don't get cancelled. This is the code I use (it is a part of - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { ):

// Create an operation without any work to do
    downloadImageOperation = [NSBlockOperation new];

    // Make a weak reference to the operation. This is used to check if the operation
    // has been cancelled from within the block
    __weak NSBlockOperation* operation = downloadImageOperation;


    // Give the operation some work to do
    [downloadImageOperation addExecutionBlock: ^() {
        // Download the image
        NSData *data = [NSData dataWithContentsOfURL:[newsimages objectAtIndex:indexPath.row]];;
        UIImage *image = [[UIImage alloc] initWithData:data];
         NSLog(@"%@",image);
        // Make sure the operation was not cancelled whilst the download was in progress
        if (operation.isCancelled) {
            return;
            NSLog(@"gestopt");
        }
        if (image != nil) {

            NSData* imageData = UIImagePNGRepresentation(image);

            [fileManager createFileAtPath:path contents:imageData attributes:nil];
            cell.imageView.image = image;
            cell.imageView.layer.masksToBounds = YES;
            cell.imageView.layer.cornerRadius = 15.0;

        }

        // Do something with the image
    }];

    // Schedule the download by adding the download operation to the queue
    [queuee addOperation:downloadImageOperation];

I've used this code to cancel:

-(void)viewDidDisappear:(BOOL)animated {
[downloadImageOperation cancel];
}

However, my NSLog tells me that even after my view dissappeared (i put an nslog there), there are still blocks.

 2012-09-12 21:32:31.869 App[1631:1a07] <UIImage: 0x3965b0>
 2012-09-12 21:32:32.508 App[1631:1907] <UIImage: 0x180d40>
 2012-09-12 21:32:32.620 App[1631:707] view dissappear!
 2012-09-12 21:32:33.089 App[1631:3a03] <UIImage: 0x3a4380>
 2012-09-12 21:32:33.329 App[1631:5a03] <UIImage: 0x198720>

Notice: there are each time 4 cells displayed in the view, so I think that even though I leave the view they are still in the queue..

Prastow
  • 178
  • 1
  • 19

1 Answers1

1

It appears that your code never has more than one block queued up - is this correct? If not then you need to send 'cancel' to the queue not to the operation.

Anyway, your problem is most likely this line:

NSData *data = [NSData dataWithContentsOfURL:[newsimages objectAtIndex:indexPath.row]];;

That download is being done for you synchronously - so if you send 'cancel' just after this message then the cancel won't be seen for a long time. This is why most developers use concurrent NSOperations using asynchronous NSURLConnections to do the downloading - so the get the cancel message in real time.

Assuming this is ARC, you can add a log in a dealloc to verify that in fact the operation has finished or been cancelled, and was properly released. [note your log above is after the return so will never get called. you should also sprinkly more isCancelled messages around so you stop as soon as you can after getting cancelled.]

David H
  • 40,852
  • 12
  • 92
  • 138
  • Well actually, for every uitableviewcell a nsblockoperation is made and an executionblock is added. This code was provided by someone at stackoverflow in a different questiontopic, but im now confused what to use. Ive tried gcd before because i saw a lot of tutorials and examplea about it but it turns out you cant cancel the block, so i dont know where to look know and im not familliar with nsblockoperation (although ive read the documentation and examples) – Prastow Sep 13 '12 at 07:56
  • You can 'sort of' cancel the block. You have a '__block BOOL cancel' ivar. Each block checks it before starting significant work. When you want to cancel, set the flag, then wait for the blocks to finish (if you use a dispatch_group you can do this). I use this all the time - the blocks finish in a few milliseconds. Its the same technique you use with concurrent NSOperations, actually. If you want to see how to do this right, I put a simple and easy to adopt project on github that uses concurrent operations and async NSURLConnections: https://github.com/dhoerl/NSOperation-WebFetches-MadeEasy – David H Sep 13 '12 at 11:35
  • I looked at the link you provided, but I can't find the __block BOOL cancel ivar you are talking about. I also read in other topics regarding blocks that you can cancel it by having a BOOL which you set to NO so no other blocks will be put in queue. I think it' just like my code if (operation.isCancelled) { right? But what if it's already in queue, and the user leaves the screen? (viewdiddissappear) How can I empty the queue (I know I can't remove the current one..) – Prastow Sep 13 '12 at 11:56
  • There is no __block in that code as it uses NSOperations, but does the fetching asynchronously so does not block like your 'dataWithContentsOfURL' does. There is no way to fix your code as that line blocks til the file is downloaded or the request fails. – David H Sep 13 '12 at 12:19