在一次面试一个ios职位时遭遇到这样一个问题,当一个tableview中许多cell中的uiimageview请求相同地址的图片时,如何保证对于同一url只进行一次网络请求,从而避免没必要的网络请求以提高运行效率。
对于这个问题,如果对于同一url请求,当任何一次请求没完成保存本地缓存时,其他请求先去查看本地缓存,这个时候是找不到的,所以也会发送网络请求,这样确实存在效率问题。 自己设计的图片请求如何解决这个问题,对于面试时的紧张局势首先想到的是使用字典以url为key,每次网络请求之前查找key,如果没有则请求网络下载,如果有则不请求,这样有一个问题急待解决,就是没有请求如何设置image并且在合适的时机设置image呢?想到的笨方法当然是当第一个请求完成并设置了本地缓存时通知另外的uiimage去再掉一次 sd_setImageWithURL 这个时候会走缓存获取效率非常高,这个方法还得继承uiimageview,在心的uiimageview中响应通知,显然这种方式很难得到面试官的认可,并不是他想要的答案,十分沮丧。回头翻阅了SDWbimage是如何实现的呢
SDWebimageDownloder中有很好的实现
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock
这个方法是实际请求网络的实现其中调用
- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock andCompletedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback
来实现相同url是否发起网络请求的判断
看一下代码比较清晰,用barrier保证顺序
dispatch_barrier_sync(self.barrierQueue, ^{
BOOL first = NO;
if (!self.URLCallbacks[url]) {
self.URLCallbacks[url] = [NSMutableArray new];
first = YES;
}
// Handle single download of simultaneous download request for the same URL
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
NSMutableDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
[callbacksForURL addObject:callbacks];
self.URLCallbacks[url] = callbacksForURL;
if (first) {
createCallback();
}
});
first变量标示是否是第一次网络请求,只有在第一次请求这个url时,才真正的发送网络请求 self.URLCallbacks这个字典保存着url为key 这个如同我说的用一个字典来表示是否请求过
巧妙在于self.URLCallbacks中的value是一个数组callbacksForURL,这是一个以字典为元素的数组,保存一些不同类型的回调如completeblock,progressblock等
网络请求结束
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
SDWebImageDownloader *sself = wself;
if (!sself) return;
NSArray *callbacksForURL = [sself callbacksForURL:url];
if (finished) {
[sself removeCallbacksForURL:url];
}
for (NSDictionary *callbacks in callbacksForURL) {
SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
if (callback) callback(image, data, error, finished);
}
}
我们分析这段代码,先取出对应url相关的callback数组,这样每个对url的请求不管是发了网络请求的还是没发网络请求的,都会响应回调从而可以设置image。
这样就完美解决一开始提出来的问题。