Steven Mok
Steven Mok

Reputation: 73

What's the correct way to retain objects in nested blocks

I want to download image for each row in a table view, so I wrote a method to do this by using block.

I thought the indexPath and tableView may be not copied by the completion block, because they are used in an If-Statement. So I retain them before the completion block excute.

and the code is:

- (void)downloadImageAtURL:(NSString *)imageURL name:(NSString *)name serial:(BOOL)serial forTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath
{
    NSMutableDictionary *cachedImagesOfURLs = self.cachedImagesOfURLs;

    UITableView *strongTableView = [tableView retain];
    NSIndexPath *strongIndexPath = [indexPath retain];

    [self.downloadManager downloadImageAtURL:imageURL
                                  identifier:[self identifierForIndexPath:indexPath]
                                      serial:serial
                                  completion:^(UIImage *image) {
                                      if (image) {
                                          [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                                              [cachedImagesOfURLs setObject:image forKey:imageURL];

                                              id cell = [strongTableView cellForRowAtIndexPath:strongIndexPath];
                                              if ([cell respondsToSelector:@selector(didDownloadImage:withName:)]) {
                                                  [cell didDownloadImage:image withName:name];
                                              }
                                          }];
                                      }

                                      [strongTableView release];
                                      [strongIndexPath release];
                                  }];
}

But the result is, when completion block excute and try to create a block and run it in main thread, it crash. The debugger print "-[XXX cellForRowAtIndexPath:]: unrecognized selector sent to instance". I seems that tableView and indexPath are deallocated.

But I don't know why, I have tried to retain them. Can somebody tell me how to prevent this crash happend? thank you very much!

Upvotes: 3

Views: 224

Answers (2)

Lithu T.V
Lithu T.V

Reputation: 20021

I think this is enough

- (void)downloadImageAtURL:(NSString *)imageURL name:(NSString *)name serial:(BOOL)serial forTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath
{
    NSMutableDictionary *cachedImagesOfURLs = self.cachedImagesOfURLs;

    [self.downloadManager downloadImageAtURL:imageURL
                                  identifier:[self identifierForIndexPath:indexPath]
                                      serial:serial
                                  completion:^(UIImage *image) {
                                      if (image) {
                                              [cachedImagesOfURLs setObject:image forKey:imageURL];

                                              id cell = [tableView cellForRowAtIndexPath:indexPath];
                                              if ([cell respondsToSelector:@selector(didDownloadImage:withName:)]) {
                                                  [cell didDownloadImage:image withName:name];
                                              }
                                      }
                                  }];
}

Upvotes: 1

danh
danh

Reputation: 62686

Even less code. Not sure how the bits of code you refer to work, but from the names, I'd guess the following is sufficient:

- (void)downloadImageAtURL:(NSString *)imageURL name:(NSString *)name serial:(BOOL)serial forTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath {

    NSMutableDictionary *cachedImagesOfURLs = self.cachedImagesOfURLs;

    [self.downloadManager downloadImageAtURL:imageURL identifier:[self identifierForIndexPath:indexPath] serial:serial completion:^(UIImage *image) {
        if (image) {
            [cachedImagesOfURLs setObject:image forKey:imageURL];
            id cell = [strongTableView cellForRowAtIndexPath:strongIndexPath];
            if ([cell respondsToSelector:@selector(didDownloadImage:withName:)]) {
                [cell didDownloadImage:image withName:name];
            }
        }
    }];
}

Upvotes: 0

Related Questions