在嵌套块中保留对象的正确方法是什么

时间:2013-03-14 04:10:38

标签: ios multithreading thread-safety objective-c-blocks

我想在表格视图中为每一行下载图像,所以我写了一个方法来使用块来完成。

我认为完成块可能不会复制indexPath和tableView,因为它们在If-Statement中使用。所以我在完成块执行之前保留它们。

,代码是:

- (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];
                                  }];
}

但结果是,当完成块执行并尝试创建一个块并在主线程中运行它时,它会崩溃。调试器打印“ - [XXX cellForRowAtIndexPath:]:无法识别的选择器发送到实例”。我似乎释放了tableView和indexPath。

但我不知道为什么,我试图保留它们。有人能告诉我如何防止这种崩溃发生吗?非常感谢你!

2 个答案:

答案 0 :(得分:1)

我认为这已经足够了

- (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];
                                              }
                                      }
                                  }];
}

答案 1 :(得分:0)

更少的代码。不确定你引用的代码是如何工作的,但是从名称来看,我猜以下就足够了:

- (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];
            }
        }
    }];
}