SDWebImage源码分析

SDWebImage是专门用于图片下载的框架,内部封装了图片异步下载和缓存的过程,涉及到的知识点有:
(1)runtime添加属性
(2)数据缓存
(3)block回调
该框架的主要功能有:
1)为Cocoa Touch框架提供一个UIImageView的分类,加载图片并进行缓存处理。
2)异步图像下载
3)异步存储器+具备自动缓存过期处理的磁盘映像缓存
4)支持GIF播放
5)支持WebP格式
6)背景图像解压缩
7)保证同一个url图片资源不被多次下载
8)保证错误url不被反复尝试下载
9)保证不会阻塞主线程
10)高性能
11)使用GCD和ARC

1 框架的结构划分

SDWebImage源码分析_第1张图片
SDWebImage源码分析_第2张图片
SDWebImage源码分析_第3张图片

2 常用的方法

2.1 图片下载核心方法

(1)图片下载核心方法的使用

       //下载并设置图片
    /*
     第一个参数:要下载图片的url地址
     第二个参数:设置该imageView的占位图片
     第三个参数:传一个枚举值,告诉程序你下载图片的策略是什么
     第一个block块:获取当前图片数据的下载进度
         receivedSize:已经下载完成的数据大小
         expectedSize:该文件的数据总大小
     第二个block块:当图片下载完成之后执行该block中的代码
         image:下载得到的图片数据
         error:下载出现的错误信息
         SDImageCacheType:图片的缓存策略(不缓存,内存缓存,沙盒缓存)
         imageURL:下载的图片的url地址
     */
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placehoder"] options:SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize) {

        //计算当前图片的下载进度
        NSLog(@"%.2f",1.0 *receivedSize / expectedSize);

    } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {

    }];

(2)图片下载核心方法的实现解析

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {

    // 取消当前图像下载
    [self sd_cancelCurrentImageLoad];

    // 利用运行时retain url
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    //判断,如果传入的下载策略不是延迟显示占位图片,那么在主线程中设置占位图片
    if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            // 设置占位图像
            self.image = placeholder;
        });
    }

    //如果url不为空
    if (url) {

        // check if activityView is enabled or not
        //检查activityView是否可用
        if ([self showActivityIndicatorView]) {
            [self addActivityIndicator];
        }

        __weak __typeof(self)wself = self;
        // 实例化 SDWebImageOperation 操作
        id  operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
            //移除UIActivityIndicatorView
            [wself removeActivityIndicator];
            if (!wself) return;

            //下面block中的操作在主线程中处理
            dispatch_main_sync_safe(^{
                if (!wself) return;
                //如果图片下载完成,且传入的下载选项为手动设置图片则直接执行completedBlock回调,并返回
                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
                {
                    completedBlock(image, error, cacheType, url);
                    return;
                }
                else if (image) {   //否则,如果图片存在,则设置图片到UIImageView上面,并刷新重绘视图
                    wself.image = image;
                    [wself setNeedsLayout];
                } else {
                    //如果没有得到图像
                    //如果传入的下载选项为延迟显示占位图片,则设置占位图片到UIImageView上面,并刷新重绘视图
                    if ((options & SDWebImageDelayPlaceholder)) {
                        wself.image = placeholder;
                        [wself setNeedsLayout];
                    }
                }
                if (completedBlock && finished) {
                    completedBlock(image, error, cacheType, url);
                }
            });
        }];
        [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
    } else {
        //如果url为空,则在主线中处理下面的操作
        dispatch_main_async_safe(^{
            //移除UIActivityIndicatorView
            [self removeActivityIndicator];

            //处理错误信息,并执行任务结束回调,把错误信息作为参数传递出去
            NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
            if (completedBlock) {
                completedBlock(nil, error, SDImageCacheTypeNone, url);
            }
        });
    }
}

(3) 图片下载并缓存的核心方法

//加载图片的核心方法
/*
 * 如果URL对应的图像在缓存中不存在,那么就下载指定的图片 ,否则返回缓存的图像
 *
 * @param url 图片的URL地址
 * @param options 指定此次请求策略的选项
 * @param progressBlock 图片下载进度的回调
 * @param completedBlock 操作完成后的回调
 *      此参数是必须的,此block没有返回值
 *      Image:请求的 UIImage,如果出现错误,image参数是nil
 *      error:如果出现错误,则error有值
 *      cacheType:`SDImageCacheType` 枚举,标示该图像的加载方式
 *          SDImageCacheTypeNone:从网络下载
 *          SDImageCacheTypeDisk:从本地缓存加载
 *          SDImageCacheTypeMemory:从内存缓存加载
 *          finished:如果图像下载完成则为YES,如果使用 SDWebImageProgressiveDownload 选项,同时只获取到部分图片时,返回 NO
 *          imageURL:图片的URL地址
 *
 * @return SDWebImageOperation对象,应该是SDWebimageDownloaderOperation实例
 */
- (id )downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {
    // Invoking this method without a completedBlock is pointless
    //没有completedblock,那么调用这个方法是毫无意义的
    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");

    // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't
    // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
    //检查用户传入的URL是否正确,如果该URL是NSString类型的,那么尝试转换
    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }

    // Prevents app crashing on argument type error like sending NSNull instead of NSURL
    //防止因参数类型错误而导致应用程序崩溃,判断URL是否是NSURL类型的,如果不是则直接设置为nil
    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }

    //初始化一个SDWebImageCombinedOperationBlock块
    __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    __weak SDWebImageCombinedOperation *weakOperation = operation;

    BOOL isFailedUrl = NO;  //初始化设定该URL是正确的

    //加互斥锁,检索请求图片的URL是否在曾下载失败的集合中(URL黑名单)
    @synchronized (self.failedURLs) {
        isFailedUrl = [self.failedURLs containsObject:url];
    }

    //如果url不正确或者 选择的下载策略不是『下载失败尝试重新下载』且该URL存在于黑名单中,那么直接返回,回调任务完成block块,传递错误信息
    if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {

        //该宏保证了completedBlock回调在主线程中执行
        dispatch_main_sync_safe(^{
            NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
            completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
        });
        return operation;
    }

    //加互斥锁,把当前的下载任务添加到『当前正在执行任务数组』中
    @synchronized (self.runningOperations) {
        [self.runningOperations addObject:operation];
    }
    //得到该URL对应的缓存KEY
    NSString *key = [self cacheKeyForURL:url];

    //该方法查找URLKEY对应的图片缓存是否存在,查找完毕之后把该图片(存在|不存在)和该图片的缓存方法以block的方式传递
    //缓存情况查找完毕之后,在block块中进行后续处理(如果该图片没有缓存·下载|如果缓存存在|如果用户设置了下载的缓存策略是刷新缓存如何处理等等)
    operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {
        //先判断该下载操作是否已经被取消,如果被取消则把当前操作从runningOperations数组中移除,并直接返回
        if (operation.isCancelled) {
            @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];
            }

            return;
        }

        //(图片不存在||下载策略为刷新缓存)且(shouldDownloadImageForURL不能响应||该图片存在缓存)
        if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
            //从此处开始,一直在处理downloaderOptions(即下载策略)
            if (image && options & SDWebImageRefreshCached) {   //如果图像存在,但是下载策略为刷新缓存,则通知缓存图像并尝试重新下载
                dispatch_main_sync_safe(^{
                    // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
                    // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
                    completedBlock(image, nil, cacheType, YES, url);
                });
            }
            // download if no image or requested to refresh anyway, and download allowed by delegate
            SDWebImageDownloaderOptions downloaderOptions = 0;
            //如果下载策略为SDWebImageLowPriority 那么downloaderOptions = 其本身
            if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
            if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
            if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
            if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
            if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
            if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
            if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
            if (image && options & SDWebImageRefreshCached) { //如果图片存在,且下载策略为刷新刷新缓存
                // force progressive off if image already cached but forced refreshing
                //如果图像已缓存,但需要刷新缓存,那么强制进行刷新
                downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
                // ignore image read from NSURLCache if image if cached but force refreshing
                //忽略从NSURLCache读取图片
                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
            }
            //到此处位置,downloaderOptions(即下载策略)处理操作结束

            //核心方法:使用下载器,下载图片
            id  subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
                if (weakOperation.isCancelled) {
                    //如果此时操作被取消,那么什么也不做
                    // Do nothing if the operation was cancelled
                    // See #699 for more details
                    // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
                }
                else if (error) { //如果下载失败,则处理结束的回调,在合适的情况下把对应图片的URL添加到黑名单中
                    dispatch_main_sync_safe(^{
                        if (!weakOperation.isCancelled) {
                            completedBlock(nil, error, SDImageCacheTypeNone, finished, url);
                        }
                    });

                    if (   error.code != NSURLErrorNotConnectedToInternet
                        && error.code != NSURLErrorCancelled
                        && error.code != NSURLErrorTimedOut
                        && error.code != NSURLErrorInternationalRoamingOff
                        && error.code != NSURLErrorDataNotAllowed
                        && error.code != NSURLErrorCannotFindHost
                        && error.code != NSURLErrorCannotConnectToHost) {
                        @synchronized (self.failedURLs) {
                            [self.failedURLs addObject:url];
                        }
                    }
                }
                else {//下载成功
                    //先判断当前的下载策略是否是SDWebImageRetryFailed,如果是那么把该URL从黑名单中删除
                    if ((options & SDWebImageRetryFailed)) {
                        @synchronized (self.failedURLs) {
                            [self.failedURLs removeObject:url];
                        }
                    }

                    //是否要进行磁盘缓存?
                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);

                    //如果下载策略为SDWebImageRefreshCached且该图片缓存中存在且未下载下来,那么什么都不做
                    if (options & SDWebImageRefreshCached && image && !downloadedImage) {
                        // Image refresh hit the NSURLCache cache, do not call the completion block
                    }
                    else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
                        //否则,如果下载图片存在且(不是可动画图片数组||下载策略为SDWebImageTransformAnimatedImage&&transformDownloadedImage方法可用)
                        //开子线程处理
                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                            //在下载后立即将图像转换,并进行磁盘和内存缓存
                            UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
                            if (transformedImage && finished) {
                                BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                                [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
                            }

                            //在主线程中回调completedBlock
                            dispatch_main_sync_safe(^{
                                if (!weakOperation.isCancelled) {
                                    completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
                                }
                            });
                        });
                    }
                    else {
                        //得到下载的图片且已经完成,则进行缓存处理
                        if (downloadedImage && finished) {
                            [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
                        }

                        dispatch_main_sync_safe(^{
                            if (!weakOperation.isCancelled) {
                                completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
                            }
                        });
                    }
                }

                if (finished) {
                    @synchronized (self.runningOperations) {
                        [self.runningOperations removeObject:operation];
                    }
                }
            }];

            //处理cancelBlock
            operation.cancelBlock = ^{
                [subOperation cancel];

                @synchronized (self.runningOperations) {
                    [self.runningOperations removeObject:weakOperation];
                }
            };
        }
        else if (image) {   //如果图片存在,且操作没有被取消,那么在主线程中回调completedBlock,并把当前操作移除
            dispatch_main_sync_safe(^{
                if (!weakOperation.isCancelled) {
                    completedBlock(image, nil, cacheType, YES, url);
                }
            });
            @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];
            }
        }
        else {
            // Image not in cache and download disallowed by delegate
            //图片不存在缓存且不允许代理下载,那么在主线程中回调completedBlock,并把当前操作移除
            dispatch_main_sync_safe(^{
                if (!weakOperation.isCancelled) {
                    completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);
                }
            });
            @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];
            }
        }
    }];

    return operation;
}

2.2 gif播放

(1)提供的接口方法

//传入Gif图像的名称,得到一个可动画的图像
+ (UIImage *)sd_animatedGIFNamed:(NSString *)name;

//传入Gif图像的二进制数据,得到一个可动画的图像
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data;

// 缩放|裁剪...
- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size;

(2)具体的实现过程

//把图片的二进制数据转换为图片(GIF)
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data {

    //如果传入的二进制数据为空,则直接返回nil
    if (!data) {
        return nil;
    }

    // 创建图像源
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);

    // 获取图片帧数
    size_t count = CGImageSourceGetCount(source);

    //初始化animatedImage
    UIImage *animatedImage;

    //如果图片帧数小于等于1,那么就直接把二进制数据转换为图片,并返回图片
    if (count <= 1) {
        animatedImage = [[UIImage alloc] initWithData:data];
    }
    else {
        //创建可变的空的图片数组
        NSMutableArray *images = [NSMutableArray array];

        //初始化动画播放时间为0
        NSTimeInterval duration = 0.0f;

        // 遍历并且提取所有的动画帧
        for (size_t i = 0; i < count; i++) {
            CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);

            //如果图片为空,则直接返回
            if (!image) {
                continue;
            }
            // 累加动画时长
            duration += [self sd_frameDurationAtIndex:i source:source];

            // 将图像添加到动画数组
            [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];

            //释放操作
            CGImageRelease(image);
        }

        //计算动画时间
        if (!duration) {
            duration = (1.0f / 10.0f) * count;
        }
        // 建立可动画图像
        animatedImage = [UIImage animatedImageWithImages:images duration:duration];
    }

    //释放操作
    CFRelease(source);

    return animatedImage;
}

//获得播放的时间长度
+ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
    float frameDuration = 0.1f;
    //获得图像的属性(图像源,索引)
    CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
    //桥接转换为NSDictionary类型
    NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
    //取出图像属性里面kCGImagePropertyGIFDictionary这个KEY对应的值,即GIF属性
    NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];

    //得到延迟时间
    NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
    if (delayTimeUnclampedProp) {
        //把延迟时间转换为浮点数类型
        frameDuration = [delayTimeUnclampedProp floatValue];
    }
    else {
        //如果上面获得的延迟时间为空,则换另外一种方式获得
        NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
        if (delayTimeProp) {
            frameDuration = [delayTimeProp floatValue];
        }
    }

    // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
    // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
    // a duration of <= 10 ms. See  and 
    // for more information.

    //处理延迟时间
    if (frameDuration < 0.011f) {
        frameDuration = 0.100f;
    }

    //释放操作
    CFRelease(cfFrameProperties);
    return frameDuration;
}

//处理GIF图片
+ (UIImage *)sd_animatedGIFNamed:(NSString *)name {
    //获得scale
    CGFloat scale = [UIScreen mainScreen].scale;

    if (scale > 1.0f) {
        //根据图片的名称拼接bundle全路径
        NSString *retinaPath = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"gif"];

        //加载指定路径的图片(二进制数据)
        NSData *data = [NSData dataWithContentsOfFile:retinaPath];

        //如果data不为空,则直接调用sd_animatedGIFWithData返回一张可动画的图片
        if (data) {
            return [UIImage sd_animatedGIFWithData:data];
        }
        //下面的处理和上面一样
        NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"];

        data = [NSData dataWithContentsOfFile:path];

        if (data) {
            return [UIImage sd_animatedGIFWithData:data];
        }

        return [UIImage imageNamed:name];
    }
    else {
        NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"];

        NSData *data = [NSData dataWithContentsOfFile:path];

        if (data) {
            return [UIImage sd_animatedGIFWithData:data];
        }

        return [UIImage imageNamed:name];
    }
}

//缩放|裁剪...
- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size {
    //如果尺寸相等或者是为0则直接返回
    if (CGSizeEqualToSize(self.size, size) || CGSizeEqualToSize(size, CGSizeZero)) {
        return self;
    }

    CGSize scaledSize = size;
    CGPoint thumbnailPoint = CGPointZero;

    CGFloat widthFactor = size.width / self.size.width;
    CGFloat heightFactor = size.height / self.size.height;
    CGFloat scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor;
    scaledSize.width = self.size.width * scaleFactor;
    scaledSize.height = self.size.height * scaleFactor;

    if (widthFactor > heightFactor) {
        thumbnailPoint.y = (size.height - scaledSize.height) * 0.5;
    }
    else if (widthFactor < heightFactor) {
        thumbnailPoint.x = (size.width - scaledSize.width) * 0.5;
    }

    //初始化可变的缩放图像数组
    NSMutableArray *scaledImages = [NSMutableArray array];

    //遍历图片
    for (UIImage *image in self.images) {
        //开启图像上下文
        UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
        //画图,把image绘制到指定的位置
        [image drawInRect:CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledSize.width, scaledSize.height)];
        //根据当前图形上下文获得一张新的图片
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        //把新图片添加到图像数组
        [scaledImages addObject:newImage];

        //关闭图形上下文
        UIGraphicsEndImageContext();
    }
    
    //建立可动画的图像并返回
    return [UIImage animatedImageWithImages:scaledImages duration:self.duration];
}

3 内存和缓存相关的问题

  • (1) 系统级内存警告如何处理(面试)
    //取消当前正在进行的所有下载操作
    [[SDWebImageManager sharedManager] cancelAll];

    //清除缓存数据(面试)
  //  cleanDisk:删除过期的文件数据,计算当前未过期的已经下载的文件数据的大小,如果发现该数据大小大于我们设置的最大缓存数据大小,那么程序内部会按照按文件数据缓存的时间从远到近删除,直到小于最大缓存数据为止。
  [[SDWebImageManager sharedManager].imageCache cleanDisk];

 // clearMemory:直接删除文件,重新创建新的文件夹
    [[SDWebImageManager sharedManager].imageCache clearMemory];

   // SDWebImage默认的缓存时间是1周
    // 当接收到内存警告之后,内部会自动清理内存缓存
  • (2) 内部如何进行缓存处理
//使用SDImageCache类专门管理缓存,该类提供了很多缓存处理的方法

// 根据给定的Key对图片的二进制数据进行磁盘缓存
- (void)storeImageDataToDisk:(NSData *)imageData forKey:(NSString *)key;

// 异步查询磁盘缓存
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;

// 同步查询内存缓存
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key;

// 查询内存缓存之后同步查询磁盘缓存
- (UIImage *)imageFromDiskCacheForKey:(NSString *)key;

// 同步从内存和磁盘缓存删除图像
- (void)removeImageForKey:(NSString *)key;

// 异步的从内存和磁盘缓存删除图像
//  当图片被删除后会调用该block块
- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion;

// 异步从内存和可选磁盘缓存删除图像
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk;

//  异步的从内存和可选磁盘缓存中删除图片
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion;

//删除图片的所有内存缓存
- (void)clearMemory;

// 删除所有磁盘缓存中的图片
- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion;

// 删除所有的磁盘缓存
- (void)clearDisk;

//从磁盘中删除所有过期的缓存图像
- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock;

// 从磁盘中删除所有过期的缓存图像
- (void)cleanDisk;

//获得磁盘缓存占用空间
- (NSUInteger)getSize;

// 获得磁盘缓存图像的数量(count)
- (NSUInteger)getDiskCount;

// 异步计算磁盘缓存的大小
- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock;

//异步检查图像是否已经在磁盘缓存中存在(不加载图像)
- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;

//检查图像是否已经在磁盘缓存中存在(不加载图像)
- (BOOL)diskImageExistsWithKey:(NSString *)key;

4 其它面试常见问题

    (1) 如何判断当前图片类型,只判断图片二进制数据的第一个字节
    + (NSString *)sd_contentTypeForImageData:(NSData *)data;

   (2)沙盒缓存图片的命名方式为对该图片的URL进行MD5加密  echo -n "url" |MD5
   
    (3) 图片的下载顺序,默认是先进先出的

SDWebImage源码更全面的解析:
https://github.com/HanGangAndHanMeimei/SDWebImage-About

你可能感兴趣的:(SDWebImage源码分析)