在UITableView中,为屏幕已关闭的单元格取消GCD操作的最佳方法是什么?

Ste*_*ven 6 iphone objective-c uitableview grand-central-dispatch ios

我有一个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];
                }
            });
        }
    });
Run Code Online (Sandbox Code Playgroud)

runQ是BOOL我设置为NOon 的ivar viewWillDisappear,它(我认为)具有在UITableView弹出导航控制器时快速刷新队列的效果.

那么,回到我原来的问题:如何取消已经关闭屏幕的单元格的图像获取操作?谢谢.

Chr*_*lay 7

首先,不要在滚动时排队操作.相反,只viewDidLoad在用户停止滚动时和当用户停止滚动时加载图像:

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

如果您仍希望能够取消对隐形单元格的加载,则可以使用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];
}
Run Code Online (Sandbox Code Playgroud)

operations是一个NSMutableDictionary.如果要取消操作,请按单元格检索indexPath,取消它,然后将其从字典中删除:

NSValue *operationHolder = [self.operations objectForKey:indexPath];
NSOperation *operation = [operationHolder nonretainedObjectValue];
[operation cancel];
Run Code Online (Sandbox Code Playgroud)