iphone - 在 UITableView 中,取消屏幕外单元格的 GCD 操作的最佳方法是什么?

标签 iphone objective-c ios uitableview grand-central-dispatch

我有一个 UITableView,它使用 GCD 将图像从 URL 异步加载到单元格中。问题是,如果用户轻拂超过 150 行,则有 150 个操作排队并执行。我想要的是出队/取消那些过去并离开屏幕的。

我该怎么做?

此时我的代码(相当标准):

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    // after getting the cell...

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if (runQ) {
            NSString *galleryTinyImageUrl = [[self.smapi getImageUrls:imageId imageKey:imageKey] objectForKey:@"TinyURL"];
            NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:galleryTinyImageUrl]];
            dispatch_async(dispatch_get_main_queue(), ^{
                if (imageData != nil) {
                    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
                    cell.imageView.image = [UIImage imageWithData:imageData];
                }
            });
        }
    });

runQ 是一个 BOOL ivar,我在 viewWillDisappear 上设置为 NO,(我认为)它具有快速清除队列的效果当此 UITableView 从导航 Controller 弹出时。

那么,回到我最初的问题:如何取消对已离开屏幕的单元格的图像获取操作?谢谢。

最佳答案

首先,不要在滚动时排队操作。相反,仅在 viewDidLoad 和用户停止滚动时为可见行加载图像:

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    for (NSIndexPath *indexPath in [self.tableView indexPathsForVisibleRows]) {
        [self loadImageForCellAtPath:indexPath];
    }
}

如果您仍然希望能够取消对不可见单元格的加载,您可以使用 NSBlockOperation 而不是 GCD:

self.operationQueue = [[[NSOperationQueue alloc] init] autorelease];
[self.operationQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];

// ...

-(void)loadImageForCellAtPath:(NSIndexPath *)indexPath {
    __block NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        if (![operation isCancelled]) {
            NSString *galleryTinyImageUrl = [[self.smapi getImageUrls:imageId imageKey:imageKey] objectForKey:@"TinyURL"];
            NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:galleryTinyImageUrl]];
            dispatch_async(dispatch_get_main_queue(), ^{
                if (imageData != nil) {
                    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
                    cell.imageView.image = [UIImage imageWithData:imageData];
                }
            });
        }
    }];

    NSValue *nonRetainedOperation = [NSValue valueWithNonretainedObjectValue:operation];
    [self.operations addObject:nonRetainedOperation forKey:indexPath];
    [self.operationQueue addOperation:operation];
}

这里的 operations 是一个 NSMutableDictionary。当你想取消一个操作时,你可以通过单元格的 indexPath 检索它,取消它,并将它从字典中删除:

NSValue *operationHolder = [self.operations objectForKey:indexPath];
NSOperation *operation = [operationHolder nonretainedObjectValue];
[operation cancel];

关于iphone - 在 UITableView 中,取消屏幕外单元格的 GCD 操作的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10408611/

相关文章:

ios - 在 View Controller 之间传递数据

ios - 大全景到平移视频

android - 从 Android/iOS 上的另一个应用程序启动 PWA 添加到主屏幕

iphone - 以 72 dpi 保存 UIImage(Retina Simulator 以 144 dpi 保存)

iphone - 单击按钮时如何显示删除取消弹出窗口

iphone - 核心数据获取属性。更新后刷新

objective-c - 我怎样才能为给定的 ViewController 提供备用的 nib 文件并即时更改它们?

iphone - UIScrollView - 如何消除滚动之前的延迟?

ios - 将 Swift 文件添加到我的项目会导致无效模块错误

android - 使溢出滚动条在 iOS 和 Android 移动设备上可见