• SDWebImage3.7.5源码阅读二



    3. downloadImageWithURL下载方法的具体实现

    方法在SDWebImageManager.m中

    id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url 	
    options:(SDWebImageOptions)options 
    progress:(SDWebImageDownloaderProgressBlock)progressBlock 
    completed:(SDWebImageCompletionWithFinishedBlock)completedBlock 
    

    参数:

    • url
    • options
      • 之前已经介绍了 SDWebImageOptions
    • progressBlock
      • SDWebImageDownloaderProgressBlock 定义在 SDWebImageDownloader.h 中具体实现为:

          //从名字可以看出来第一个参数是已经接受了数据的大小
          //另一个参数表示总数据的大小
          typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);
        
    • completedBlock
      • 图片下载完要做的块 具体实现为:

          typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL);
        

    然后一看这个方法实现……麻蛋 好长!

    3.1 一些判断

    这个没什么好说的

    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

    __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    __weak SDWebImageCombinedOperation *weakOperation = operation;   
    

    它的具体实现:

    //头
    @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];
    }
    }
    
    //cacheOperation 对应的到底是 下载操作还是 缓存相关的操作。。
    - (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

    //判断是否是已经下载失败的url
    BOOL isFailedUrl = NO;
    @synchronized (self.failedURLs) {
        isFailedUrl = [self.failedURLs containsObject:url];
    }
    //当url是失败过的url并且options不是SDWebImageRetryFailed 时直接报错    
    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实例

    //将之前生成的 SDWebImageCombinedOperation *operation
    //存入 runningOperations
    @synchronized (self.runningOperations) {
    	[self.runningOperations addObject:operation];
    }
    

    之前不是猜测 SDWebImageCombinedOperation中的 cacheOperation是缓存还是下载的操作吗,到这里就可以猜出来,应该是缓存和下载操作都有,因为要是只是缓存操作的话,这个操作不会进行很久,一般也不需要储存起来管理。

    //应该是生成图片缓存路径对应的key
    NSString *key = [self cacheKeyForURL:url];
    

    来看看它的实现:

    - (NSString *)cacheKeyForURL:(NSURL *)url {
    if (self.cacheKeyFilter) {
        return self.cacheKeyFilter(url);
    }
    else {
        return [url absoluteString];
    }
    }	
    

    其中 cacheKeyFilter是过滤url用的,它是个SDWebImageCacheKeyFilterBlock块,作者注解中写的很清楚了,它可以用来删除url中动态生成的部分,比如一些“?”之后的参数什么的,但是我没用到,这里就不讨论如何自定义这个SDWebImageCacheKeyFilterBlock块了。

    接下来看吧:

    operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {
    
    ...
    //一大串 块内容,
    }];
    
    return operation;
    

    这里终于要开始实现cacheoperation了。。

    其中self.imageCache 是在init方法中生成的 就是生成一个SDImageCache的单例:

    - (SDImageCache *)createCache {
    	return [SDImageCache sharedImageCache];
    }
    

    接着来看queryDiskCacheForKey:

    - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
    ...
    
    //在内存中查看是否存在
    //其实就是在NSCache类的一个memCache对象中查找
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image) {
        doneBlock(image, SDImageCacheTypeMemory);
        return nil;
    }
    
    NSOperation *operation = [NSOperation new];
    //ioQueue就是SDImageCache初始化时生成一个GCD并行队列
    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的单例(流程之外)

    一开始看见它的单例是这么写,这不一看就知道不是严格的单例吗

    + (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使创建出来的对象严格保持单例,结果实验了一下:

        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方法就不会错。。

  • 相关阅读:
    面试知识点2
    面试知识点3
    面试知识记录
    JQuery手写一个简单的轮播图
    推荐一款好用的日历插件
    JQuery获取复选框的值
    JQuery手写一个简单的分页
    JQuery给一个元素绑定两次点击事件(第二次点击事件)
    懒加载预加载(图片)
    JQuery Ajax 使用FormData上传文件对象
  • 原文地址:https://www.cnblogs.com/sunyanyan/p/5394811.html
Copyright © 2020-2023  润新知