1

I my application I have table view with custom cell, that has:

@property (weak, nonatomic) IBOutlet UIImageView *image;
@property (weak, nonatomic) IBOutlet UILabel *nameOfImage;
@property (weak, nonatomic) IBOutlet UIButton *start;

I need to download any number of images asynchronously (by pressing a lot of start buttons). For this purpose I created subclass of NSOperation - MyOperationQueue. implementation of it looks like this:

- (id)initWithURL:(NSURL*)url andRaw:(NSInteger)row
{
    if (![super init])
        return nil;
    [self setTargetURL:url];
    [self setCurrentCell:row];
    self.customCell = [[CustomTableViewCell alloc]init];
    self.tableView = [ContentTableView sharedManager];
    return self;
}

- (void)main
{
    if ([self isCancelled])return;
    self.defaultSession = [self configureSession];
    NSURLSessionDownloadTask *task = [self.defaultSession downloadTaskWithURL:self.targetURL];
    if ([self isCancelled])  return;
    [task resume];
 }

- (void)cancel
{
    self.isCancelled = YES;
       if(self.downloadTask.state == NSURLSessionTaskStateRunning)
        [self.downloadTask cancel];
}

among this methods I have also described here NSURLSession methods:

    - (NSURLSession *) configureSession //it visits it
    {
 NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self.tableView **delegateQueue:self**];  //self - warning - incompatible pointer type sending "MyOperationQueue" to "NSOperationQueue" _Nullable.

}

-(void)URLSession:(NSURLSession *)session
         downloadTask:(NSURLSessionDownloadTask *)downloadTask
         didWriteData:(int64_t)bytesWritten
    totalBytesWritten:(int64_t)totalBytesWritten
    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite // don't visit

-(void)URLSession:(NSURLSession *)session
     downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location    // don't visit

I create the custom queue in method in TableView:

   - (void)didClickStartAtIndex:(NSInteger)cellIndex withData:(CustomTableViewCell*)data
    {
        NSURL *url = [NSURL URLWithString: tmp.imeageURL];
        MyOperationQueue * one = [[MyOperationQueue alloc]initWithURL:url andRaw:self.selectedCell];
        [self.queue addOperation:one];
    }

The image is not loaded at all, the queue just enter into configuration method and thats all. Please advise me whats wrong? Thank you in advance.

EDIT: Updating UI:

-(void)URLSession:(NSURLSession *)session
     downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    NSLog(@"didFinishDownloadingToURL - Queue");
    NSData *d = [NSData dataWithContentsOfURL:location];
    UIImage *im = [UIImage imageWithData:d];
    dispatch_async(dispatch_get_main_queue(), ^{
        self.customCell.image.image = im;
        self.customCell.realProgressStatus.text = @"Downloaded";
        [self.tableView.tableView reloadData];
    });
}
Melany
  • 466
  • 7
  • 20
  • BTW, in your `cancel` method, you probably want to call `[super cancel]` so that you'll enjoy the standard `NSOperation` behavior. Also, in `main`, make sure to do the `isFinished` KVO. Finally, I assume you defined your `NSOperation` class to be asynchronous? – Rob Mar 30 '16 at 23:33
  • @Rob, I just created the subclass of it, I never mentioned that it should be asynchronous, where should I do that? – Melany Mar 30 '16 at 23:36
  • Yes, it must be asynchronous because `NSURLSession` is asynchronous. So (a) `isAsynchronous` must return `true`; (b) you have to do `isExecuting` and `isFinished` KVO and have similarly named methods that return the correct values. If you search for "asynchronous `NSOperation` subclass", you'll likely find lots of hits on all of that stuff. But you definitely need to make this asynchronous operation or else your operation will terminate and be deallocated before any of the delegate methods are called. – Rob Mar 30 '16 at 23:40
  • @Rob thank you very much, finally I started to understand whats going wrong, i correct all according to your advices. – Melany Mar 30 '16 at 23:43

1 Answers1

1

Your class is a subclass of NSOperation, not NSOperationQueue So you can't specify it to use self as the target queue for the NSURLSession object. Frankly, you'd just use nil for the operation queue parameter, as you don't care which on which queue the delegate methods run. Or you can create your own operation queue, if you want. But you cannot use self.

But make sure to dispatch any UI updates back to the main queue.

Also, you must make this an asynchronous NSOperation subclass or else the operation will terminated and be deallocated before your delegate methods ever get a chance to be called.

As a minor observation, I'd suggest renaming this class to be MyOperation, to avoid confusion between operations and operation queues.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • thank you, So I should inside of main method create an instance of NSOperation queue and add it instead of self? – Melany Mar 30 '16 at 23:22
  • In all tutorials I saw that there a need to create a subclass of NSOperation, not OperationQueue – Melany Mar 30 '16 at 23:23
  • Subclassing `NSOperation` is corrrect. But for the `queue` parameter of `NSURLSession`, I'd just use `nil`, as you probably don't care which queue `NSURLSession` uses. (Don't confuse the queue for your operations with the queue that `NSURLSession` uses.) Just make sure to dispatch UI updates back to the main queue. – Rob Mar 30 '16 at 23:27
  • Thank you, I have updated a question, on the bottom I just reflected 1 method of NSURLSession, that locates in MyOperation - can I update UI in such a way? – Melany Mar 30 '16 at 23:32
  • and by the way even after I set delegate as new, it still doesnt visit methods beside configuration. What could be the reason? When it is done on main queue everything works perfectly. – Melany Mar 30 '16 at 23:34
  • Yep, it has to be asynchronous op, as we discussed above. – Rob Mar 31 '16 at 00:28