在UITableView,最好的方法来取消已经离开屏幕的单元格的GCD操作?

我有一个UITableView ,它使用GCDasynchronous地将URL中的图像加载到单元格中。 问题是如果用户popup150行,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伊娃我viewWillDisappear设置为NO ,这(我认为)具有快速冲出队列,当这个UITableViewpopup导航控制器的效果。

所以,回到我原来的问题:我如何取消已经离开屏幕的单元格的图像获取操作? 谢谢。

首先,不要在滚动时排队操作。 而是在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]; 

分派后您不能取消GCD操作。 但是,您可以检查分派块以查看代码是否需要继续。 在-tableView:cellForRowAtIndexPath:你可以做这样的事情

 UIImage *image = [imageCache objectForKey:imageName]; if(image) { cell.imageView.image = image; else { dispatch_async(globalQueue, ^{ //get your image here... UIImage *image = ... dispatch_async(dispatch_get_main_queue(), ^{ if([cell.indexPath isEqual:indexPath){ cell.indexPath = nil; cell.imageView.image = image; [cell setNeedsLayout]; } }); [imageCache setObject:image forKey:imageName]; }); } 

基本上使用图像caching(NSMutableDictionary),并试图抓住图像。 如果你没有它,然后派出一个块,并得到它。 然后发回主线程,检查索引path,然后设置图像,最后caching图像。 这个例子使用一个自定义的tableview单元格,它的索引path和它一起存储为一个属性。

所以,它的一些额外的工作,使这项工作很好,但它的价值。

如果您使用的是出队单元格,最好的方法是取消GCD

– [UITableViewCell prepareForReuse]