3. downloadImageWithURL下载方法的具体实现 方法在SDWebImageManager.m中
1 2 3 4 id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionWithFinishedBlock)completedBlock
参数:
url
options
之前已经介绍了 SDWebImageOptions
progressBlock
SDWebImageDownloaderProgressBlock 定义在 SDWebImageDownloader.h 中具体实现为: 1 2 3 typedef void (^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);
completedBlock
图片下载完要做的块 具体实现为: 1 2 typedef void (^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL);
然后一看这个方法实现……麻蛋 好长!
3.1 一些判断 这个没什么好说的
1 2 3 4 5 6 7 8 9 10 NSAssert (completedBlock != nil , @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead" ); if ([url isKindOfClass:NSString .class]) { url = [NSURL URLWithString:(NSString *)url]; } if (![url isKindOfClass:NSURL .class]) { url = nil ; }
3.2 SDWebImageCombinedOperation
下来看到了一个类 SDWebImageCombinedOperation
1 2 3 __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new]; __weak SDWebImageCombinedOperation *weakOperation = operation;
它的具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation >@property (assign , nonatomic , getter = isCancelled) BOOL cancelled;@property (copy , nonatomic ) SDWebImageNoParamsBlock cancelBlock;@property (strong , nonatomic ) NSOperation *cacheOperation;@end @implementation SDWebImageCombinedOperation - (void )setCancelBlock:(SDWebImageNoParamsBlock)cancelBlock { if (self .isCancelled) { if (cancelBlock) { cancelBlock(); } _cancelBlock = nil ; } else { _cancelBlock = [cancelBlock copy ]; } } - (void )cancel { self .cancelled = YES ; if (self .cacheOperation) { [self .cacheOperation cancel]; self .cacheOperation = nil ; } if (self .cancelBlock) { self .cancelBlock(); _cancelBlock = nil ; } } @end
它实现了 SDWebImageOperation好歹是回答了之前的问题3
回答问题3(部分):SDWebImageOperation的实现之一是SDWebImageCombinedOperation
但是这个cacheOperation 命名令我很困惑。。因为到现在还不知道下载操作会放在那里。。
问题7 : SDWebImageCombinedOperation的cacheOperation执行什么操作
然后它的属性cancelBlock是 长这样的 typedef void(^SDWebImageNoParamsBlock)();
好像没什么用的样子。。
3.3 isFailedUrl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 BOOL isFailedUrl = NO ; @synchronized (self .failedURLs) { isFailedUrl = [self .failedURLs containsObject:url]; } if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { dispatch_main_sync_safe(^{ NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil ]; completedBlock(nil , error, SDImageCacheTypeNone, YES , url); }); return operation; }
这里 终于解决了之前的第四个问题,failedURLs就储存了失效的url,也就是依据这个做出不重复下载的功能
回答问题4: 不重复下载相同url 是根据SDWebImageManager.failedURLs来实现的
看到这里 还没有看见下载的功能。。判断了这么多条件 真是值得学习啊。。
3.4 储存operation,生成cacheOperation实例 1 2 3 4 5 6 @synchronized (self .runningOperations) { [self .runningOperations addObject:operation]; }
之前不是猜测 SDWebImageCombinedOperation中的 cacheOperation是缓存还是下载的操作吗,到这里就可以猜出来,应该是缓存和下载操作都有,因为要是只是缓存操作的话,这个操作不会进行很久,一般也不需要储存起来管理。
1 2 3 NSString *key = [self cacheKeyForURL:url];
来看看它的实现:
1 2 3 4 5 6 7 8 9 - (NSString *)cacheKeyForURL:(NSURL *)url { if (self .cacheKeyFilter) { return self .cacheKeyFilter(url); } else { return [url absoluteString]; } }
其中 cacheKeyFilter是过滤url用的,它是个SDWebImageCacheKeyFilterBlock块,作者注解中写的很清楚了,它可以用来删除url中动态生成的部分,比如一些“?”之后的参数什么的,但是我没用到,这里就不讨论如何自定义这个SDWebImageCacheKeyFilterBlock块了。
接下来看吧:
1 2 3 4 5 6 7 8 operation.cacheOperation = [self .imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) { ... }]; return operation;
这里终于要开始实现cacheoperation了。。
其中self.imageCache 是在init方法中生成的 就是生成一个SDImageCache的单例:
1 2 3 4 - (SDImageCache *)createCache { return [SDImageCache sharedImageCache]; }
接着来看queryDiskCacheForKey:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock { ... UIImage *image = [self imageFromMemoryCacheForKey:key]; if (image) { doneBlock(image, SDImageCacheTypeMemory); return nil ; } NSOperation *operation = [NSOperation new]; dispatch_async (self .ioQueue, ^{ if (operation.isCancelled) { return ; } @autoreleasepool { UIImage *diskImage = [self diskImageForKey:key]; if (diskImage && self .shouldCacheImagesInMemory) { NSUInteger cost = SDCacheCostForImage(diskImage); [self .memCache setObject:diskImage forKey:key cost:cost]; } dispatch_async (dispatch_get_main_queue(), ^{ doneBlock(diskImage, SDImageCacheTypeDisk); }); } }); return operation; }
我这流程中 图片是第一次下载的,所以就按着 内存和本地中都找不到流程走。
看到这里,有点奇怪为什么这里要加autorealeasepool,看了下资料,说是可以优化内存。
autorealeasepool机制参考链接
autorealeasepool机制参考链接2
3.5 关于SDWebImageManager的单例(流程之外) 一开始看见它的单例是这么写,这不一看就知道不是严格的单例吗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + (id )sharedManager { static dispatch_once_t once; static id instance; dispatch_once (&once, ^{ instance = [self new]; }); return instance; } - (id )init { if ((self = [super init])) { ... } return self ; }
一开始我还以为是用了什么高级的Runtime使创建出来的对象严格保持单例,结果实验了一下:
1 2 3 4 5 6 SDWebImageManager* m1 = [SDWebImageManager sharedManager]; NSLog (@" m1 :%@ " ,m1);SDWebImageManager* m2 = [[SDWebImageManager alloc]init]; NSLog (@" m2 :%@ " ,m2);
打印出来:
sdwebImageTest[4350:1125408] m1 :<SDWebImageManager: 0x17550510>
sdwebImageTest[4350:1125408] m2 :<SDWebImageManager: 0x175537e0>
摔!这不就是《只要你确保只调用sharedManager就确保单例》的做法吗。。作者开心就好。。反正里面的单例模式大家都只调用默认的sharedManager方法就不会错。。