开始在cell中是使用这个函数来加载图片的。
[self.photoView sd_setImageWithURL:[NSURL URLWithString: [post objectForKey: @"thumb_url"]] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {...}
非常简洁的API,好舒服。但是在tableview第一次加载数据的时候,屏幕第二个cell的图片总是无法显示,通过打日志发现,第二个cell的图片的请求并没回调completed接口,后续很多cell的图片请求都没有回调,被取消了。看了一下源码,才知道,这个API的请求会取消掉之前产生图片下载请求任务。比如我们reloadData,加载了20个CELL的数据,产生了20个图片下载请求,但是实际上可能真正执行的也就是第一个和最后一个,中间的下载任务由于是队列中的任务,还没有真正发出去就被取消掉了。
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock { [self sd_cancelCurrentImageLoad];
解决的办法,就是使用稍微复杂一点的API
@interface TFeedCell()@property (nonatomic) idphotoDownloadOperation;@end- (void)setPost: (NSDictionary *)post shoudDownloadImage: (BOOL)shouldDownload { _post = post;//其他UI元素设置。。。 self.photoView.image = nil; //由于我使用了FDTemplateCell开源项目,setPost方法会在tableView delegate的 cellForRow和HeightForRow都被调用,在heightForRow调用setPost不需要去做图片下载,会产生较多的重复下载,所以增加了这个参数。 if (shouldDownload) { if (self.photoDownloadOperation) { [self.photoDownloadOperation cancel]; } self.photoDownloadOperation = nil; WS(ws); self.photoDownloadOperation = [[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString: [post objectForKey: @"thumb_url"]] options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { if (!error) { ws.photoView.image = image; } else { DDLogDebug(@"%@", error); } }]; }}
downloadImageWithURL这个方法不会取消之前产生的任务。但是我们在加载20个cell的时候并不希望20个图片都下载,所以需要自己管理下载任务,需要时才下载。
因为cell是重用的,虽然reloadData加载了20个数据(假设一个网络请求20条数据),但实际上产生的cell不过几个,取决于屏幕可见大小,看不见的部分理论上有十几个cell实际上都是重用了同一个cell。利用这个机制,只要cell被加载的时候,我们就把当前cell关联的图片下载请求取消掉,产生新的图片下载请求,那么实际上真正有效的图片下载请求只有屏幕可见的几个cell和最后一个看不见的cell。从用户使用的角度感觉就是边滚动边下载。