【问题标题】:What's the correct way to retain objects in nested blocks在嵌套块中保留对象的正确方法是什么
【发布时间】:2013-03-14 04:10:37
【问题描述】:

我想为表格视图中的每一行下载图像,所以我编写了一个方法来使用 block.

我认为 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:]: unrecognized selector sent to instance”。我似乎 tableView 和 indexPath 被释放了。

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

【问题讨论】:

  • 但是需要保留这个吗?你有没有直接在块中尝试过方法变量?我认为它可以工作
  • 块复制它需要的变量。不需要显式保留。只是不要在块内引用 self.downloadManager。
  • 你真的应该切换到 ARC!
  • @Till Yep...我真的这么认为。但我必须支持iOS 4.3,而iOS 4.3 SDK 不支持弱属性。当我的部署目标升级到 iOS 5 时,我将使用 ARC...
  • 您可以在 iOS4.3 上使用 unsafe_unretained,因此 ARC 完全可以在该操作系统上使用 - 只是没有它的所有功能。

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


【解决方案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];
                                              }
                                      }
                                  }];
}

【讨论】:

  • 可能不需要向mainQueue添加操作,完成块本身很可能运行在主线程上。
  • “完成块本身可能在主线程上运行”是什么让你这么认为?
  • @newacct,我不确定是不是这样,但如果我正在编写一个“下载管理器”,比如 NSURLConnection,我会在主线程上调用该块。我会告诉我的调用者,完成块可以用来更新 UI,所以为了方便我会在 main 上调用它。
  • @LithuT.V 完成块不在主线程上运行,它在开始下载的线程上运行。我认为这个问题不是由这个引起的。不过还是谢谢你们。
  • @danh 你是对的。我应该在主线程上运行完成块。但问题不在于这个。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-09
    • 1970-01-01
    • 2020-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多