SDWebImage的技术分享

SDWebImage图片的缓存的处理大部分都是在 SDImageCache 这个类中实现,SDWebImage 的图片缓存采用的是 Memory 和 Disk 双重缓存机制。

/**
 * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed
 * asynchronous so it doesn’t add unnecessary latency to the UI.
 */
//SDImageCache维护一个内存缓存和一个可选的磁盘缓存。执行磁盘缓存写入操作是
异步的,所以它不会给UI增加不必要的延迟

@interface SDImageCache : NSObject

@property (strong, nonatomic, nonnull) SDMemoryCache *memCache;

@end

@interface SDMemoryCache  : NSCache 

@end

  • 内存缓存
    内存缓存是使用SDMemoryCache(继承自系统的NSCache)实现的,它是一个类似于 NSDictionary 的集合类,用于在内存中存储我们要缓存的数据。

  • 磁盘缓存

- (void)_storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
    if (!imageData || !key) {
        return;
    }
    
    if (![self.fileManager fileExistsAtPath:_diskCachePath]) {
        [self.fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
    }
    
    // get cache Path for image key
    NSString *cachePathForKey = [self defaultCachePathForKey:key];
    // transform to NSUrl
    NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
    
    [imageData writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil];
    
    // disable iCloud backup
    if (self.config.shouldDisableiCloud) {
        [fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
    }
}

将图片存放到沙盒的NSCachesDirectory 目录中

- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    return [paths[0] stringByAppendingPathComponent:fullNamespace];
}

为每一个缓存文件生成一个 md5 文件名, 存放到文件中

- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path {
    NSString *filename = [self cachedFileNameForKey:key];
    return [path stringByAppendingPathComponent:filename];
}


- (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key {
    const char *str = key.UTF8String;
    if (str == NULL) {
        str = "";
    }
    unsigned char r[CC_MD5_DIGEST_LENGTH];
    CC_MD5(str, (CC_LONG)strlen(str), r);
    NSURL *keyURL = [NSURL URLWithString:key];
    NSString *ext = keyURL ? keyURL.pathExtension : key.pathExtension;
    // File system has file name length limit, we need to check if ext is too long, we don't add it to the filename
    if (ext.length > SD_MAX_FILE_EXTENSION_LENGTH) {
        ext = nil;
    }
    NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
                          r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
                          r[11], r[12], r[13], r[14], r[15], ext.length == 0 ? @"" : [NSString stringWithFormat:@".%@", ext]];
    return filename;
}

缓存清理

SDWebImage 会在每次 APP 结束和进入后台的时候执行清理任务。 清理缓存分两步进行。

  1. 先清除掉过期的缓存文件。 如果清除掉过期的缓存之后,空间还不够
  2. 按文件时间从早到晚排序,先清除最早的缓存文件,直到剩余空间达到要求
//初始化时添加观察者
[[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(deleteOldFiles)
                                                     name:UIApplicationWillTerminateNotification
                                                   object:nil];
 [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(backgroundDeleteOldFiles)
                                                     name:UIApplicationDidEnterBackgroundNotification
                                                   object:nil];

static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week

if (self.config.maxCacheSize > 0 && currentCacheSize > self.config.maxCacheSize) { 
//只有在 maxCacheSize 大于 0 并且当前缓存空间大于 maxCacheSize 的时候才进行第二步的缓存清理
}

SDWebImage 并没有对 maxCacheSize 设置默认值。SDWebImage 在默认情况下不会对缓存空间设限制。
缺点:在设置-通用-储存空间中用户可以查看每个 APP 的空间使用情况, 如果你的 APP 占用空间比较大的话,就很容易成为用户的卸载目标

过滤URL,禁用缓存

如果想过滤特定URL,不使用缓存机制,可以在对应位置加入过滤代码

SDWebImageManager.sharedManager.cacheKeyFilter = ^NSString * _Nullable(NSURL * _Nullable url) {

        url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
        if ([[url absoluteString] isEqualToString:@"要过滤的URL"]) {
            return nil;
        }
        return [url absoluteString];
    };

//取缓存
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url {
    if (!url) {
        return @"";
    }

    if (self.cacheKeyFilter) {
        return self.cacheKeyFilter(url);
    } else {
        return url.absoluteString;
    }
}

清除特定图片缓存

SDWebImage加载图片优先会从缓存中取,而不是每次重新请求加载,那么如果我们的头像/广告图需要实时刷新,应该怎么解决?

  1. 使用 options:SDWebImageRefreshCached 刷新缓存
    该方法可能会有闪烁,甚至有时并没有更新图片的问题存在
  2. 每次清除掉图片缓存,重新加载的方式
    NSURL *imageURL = [NSURL URLWithString:@"实时更新的URL"];
// 获取对应URL链接的key
    NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:imageURL];
    NSString *pathStr = [[SDImageCache sharedImageCache] defaultCachePathForKey:key];
// 删除对应key的文件
    [[SDImageCache sharedImageCache] removeImageForKey:key withCompletion:^{
        [self.tempImageView sd_setImageWithURL:imageURL placeholderImage:[UIImage imageNamed:@"placeholderHead.png"]];
    }];

SDWebImage工作流程

SDWebImageManager同时管理SDImageCache和SDWebImageDownloader两个类
讲解SDWebImageManager是如何下载图片之前,我们先看一下这个类的几个重要的属性:

@property (strong, nonatomic, readwrite, nonnull) SDImageCache *imageCache;//管理缓存
@property (strong, nonatomic, readwrite, nonnull) SDWebImageDownloader //下载器*imageDownloader;
@property (strong, nonatomic, nonnull) NSMutableSet *failedURLs;//记录失效url的名单
@property (strong, nonatomic, nonnull) NSMutableArray *runningOperations;//记录当前正在执行的操作

SDWebImageOptions

SDWebImage的技术分享_第1张图片
SDWebImageOptions.png

SDWebImageScaleDownLargeImages

默认情况下,图像将根据其原始大小进行解码。 在iOS上,此标志会将图片缩小到与设备的受限内存兼容的大小。如果设置了“SDWebImageProgressiveDownload”标志,则会关闭缩小比例

SDWebImage-解码、压缩图像
参考1
参考2

SDWebImage的技术分享_第2张图片
SDWebImage的加载流程.png
  1. SDWebImage最外层的类是我们常用的UIImageView +WebCache类,下面是这个类的公共接口
 // ==============  UIImageView + WebCache.h ============== //
- (void)sd_setImageWithURL:(nullable NSURL *)url;
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder;
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options;
- (void)sd_setImageWithURL:(nullable NSURL *)url
                 completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                 completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                 completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock;

//使用方法
[imageView sd_setImageWithURL:[NSURL URLWithString:@"http://swiftcafe.io/images/qrcode.jpg"]];
  1. 最后都会调用 UIView (WebCache)分类 的方法

为什么不是UIImageView+WebCache而要上一层到UIView的分类里呢?
因为SDWebImage框架也支持UIButton的下载图片等方法,所以需要在它们的父类:UIView里面统一一个下载方法。

- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                      operationKey:(nullable NSString *)operationKey
                     setImageBlock:(nullable SDSetImageBlock)setImageBlock
                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(nullable SDExternalCompletionBlock)completedBlock
                           context:(nullable NSDictionary *)context {

 //UIView+WebCacheOperation 的 operationDictionary
 //下面这行代码是保证没有当前正在进行的异步下载操作, 使它不会与即将进行的操作发生冲突
//这里作者专门创造一个分类UIView+WebCacheOperation来管理操作缓存(字典)
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
    [self sd_cancelImageLoadOperationWithKey:validOperationKey];

//主线程设置展位图片
 if (!(options & SDWebImageDelayPlaceholder)) {
        if (group) {
            dispatch_group_enter(group);
        }
        dispatch_main_async_safe(^{
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];
        });
    }

//内部会调用 SDWebImageManager 的 loadImageWithURL 方法来处理这个图片 URL
id  operation = [manager loadImageWithURL:url options:options progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { }

 [self sd_setImageLoadOperation:operation forKey:validOperationKey];
}

UIView+WebCacheOperation分类来关联对象,管理操作缓存

 // ==============  UIView+WebCacheOperation.m ============== //
- (SDOperationsDictionary *)sd_operationDictionary {
    @synchronized(self) {
        SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey);
        if (operations) {
            return operations;
        }
        operations = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
        objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        return operations;
    }
}

- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key {
    if (key) {
        [self sd_cancelImageLoadOperationWithKey:key];
        if (operation) {
            SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
            @synchronized (self) {
                [operationDictionary setObject:operation forKey:key];
            }
        }
    }
}
  1. SDWebImageManager 内部的 downloadImageWithURL 方法会调用 SDImageCache 类的 queryCacheOperationForKey 方法,查询图片缓存
operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key options:cacheOptions done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) { }

//SDImageCache 内部的缓存查询策略
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock {

// First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    BOOL shouldQueryMemoryOnly = (image && !(options & SDImageCacheQueryDataWhenInMemory));
    if (shouldQueryMemoryOnly) {
        if (doneBlock) {
            doneBlock(image, nil, SDImageCacheTypeMemory);
        }
        return nil;
    }

//缓存没有,再查询磁盘
@autoreleasepool {
            NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
            UIImage *diskImage;
            SDImageCacheType cacheType = SDImageCacheTypeNone;
            if (image) {
                // the image is from in-memory cache
                diskImage = image;
                cacheType = SDImageCacheTypeMemory;
            } else if (diskData) {
                cacheType = SDImageCacheTypeDisk;
                // decode image data only if in-memory cache missed
                diskImage = [self diskImageForKey:key data:diskData options:options];
                if (diskImage && self.config.shouldCacheImagesInMemory) {
                    NSUInteger cost = SDCacheCostForImage(diskImage);
                    //如果 Disk Cache 查询成功,还会把得到的图片再次设置到 Memory Cache 中。 这样做可以提高查询图片的效率
                    [self.memCache setObject:diskImage forKey:key cost:cost];
                }
            }
            
            if (doneBlock) {
                if (options & SDImageCacheQueryDiskSync) {
                    doneBlock(diskImage, diskData, cacheType);
                } else {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        doneBlock(diskImage, diskData, cacheType);
                    });
                }
            }
        }

}
  1. 如果缓存查询成功, 会直接返回缓存数据。 如果不成功,再开始使用 SDWebImageDownloader 请求网络下载图片
if (url) {

SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    operation.manager = self;
[self.runningOperations addObject:operation];

__weak typeof(strongOperation) weakSubOperation = strongOperation;
strongOperation.downloadToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {}
//在操作缓存字典(operationDictionary)里添加operation,表示当前的操作正在进行
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
} else {
//如果url不存在,就在completedBlock里传入error(url为空)
       dispatch_main_async_safe(^{
           [self sd_removeActivityIndicator];
           if (completedBlock) {
               NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
               completedBlock(nil, error, SDImageCacheTypeNone, url);
           }
       });
}

4.1. 如果下载失败, 会把失败的图片地址写入 failedURLs 集合

if (shouldBlockFailedURL) {
                        LOCK(self.failedURLsLock);
                        [self.failedURLs addObject:url];
                        UNLOCK(self.failedURLsLock);
                    }

因为 SDWebImage 默认会有一个对上次加载失败的图片拒绝再次加载的机制。
failedURLs这个属性是在内存中存储的,如果图片加载失败, SDWebImage 会在本次 APP 会话中都不再重试这张图片了。当然这个加载失败是有条件的,如果是超时失败(15s),不会写入 failedURLs 集合
SDWebImage 这样做可能是为了避免不必要的资源浪费,提高性能吧。

    BOOL isFailedUrl = NO;
    if (url) {
        LOCK(self.failedURLsLock);
        isFailedUrl = [self.failedURLs containsObject:url];
        UNLOCK(self.failedURLsLock);
    }
//如果options的值没有设置为失败后重试并且url下载失败过,执行完成block返回错误
    if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
        return operation;
    }

如果在加载图片的时候设置 SDWebImageRetryFailed 标记,这样 SDWebImage 就会加载failedURLs集合中的图片。

4.2. 如果下载图片成功了,接下来就会使用 [self.imageCache storeImage:transformedImage imageData:cacheData forKey:key toDisk:cacheOnDisk completion:nil] 方法将它写入缓存和磁盘,并且调用 completedBlock 回调显示图片

[self.imageCache storeImage:transformedImage imageData:cacheData forKey:key toDisk:cacheOnDisk completion:nil];

dispatch_main_async_safe(^{
        if (operation && !operation.isCancelled && completionBlock) {
            completionBlock(image, data, error, cacheType, finished, url);
        }
    });
//如果已经完成下载了,将operation移除
   [self safelyRemoveOperationFromRunning:operation];
//[self.runningOperations removeObject:operation]; 就是这句代码


以上就是SDWebImage 的整体图片加载流程

SDWebImage的下载队列机制

  • 图片下载是通过SDWebImageDownloader和SDWebImageDownloaderOperation类来完成的。
  • SDWebImageDownloaderOperation封装了单个图片下载操作,继承自NSOperation,这个类主要实现了图片下载的具体操作、及图片下载完成后的图片解压缩、Operation生命周期管理等
  • SDWebImageDownloader是用来管理SDWebImageDownloaderOperation图片下载任务的,内部维护着一个私有并发下载队列downloadQueue,默认最大并发数是6,SDWebImage的下载队列默认情况下是SDWebImageDownloaderFIFOExecutionOrder,是先进先出的
- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration {
    if ((self = [super init])) {
        _operationClass = [SDWebImageDownloaderOperation class];
        _shouldDecompressImages = YES;
        _executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
        _downloadQueue = [NSOperationQueue new];
        _downloadQueue.maxConcurrentOperationCount = 6;
        _downloadQueue.name = @"com.hackemist.SDWebImageDownloader";
        _URLOperations = [NSMutableDictionary new];
        _downloadTimeout = 15.0;
    }
    return self;
}

//核心方法:下载图片
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {

NSOperation *operation = [self.URLOperations objectForKey:url];
[self.downloadQueue addOperation:operation];
SDWebImageDownloadToken *token = [SDWebImageDownloadToken new];
    token.downloadOperation = operation;
    token.url = url;

    return token;
}

项目使用注意事项

  1. 下载图片完成的回调不能为空,
[[SDWebImageManager sharedManager] loadImageWithURL:url
                                                    options:SDWebImageRetryFailed|SDWebImageContinueInBackground
                                                   progress:nil
                                                  completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
                                                  }];

- (id )loadImageWithURL:(nullable NSURL *)url
                                     options:(SDWebImageOptions)options
                                    progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                   completed:(nullable SDInternalCompletionBlock)completedBlock {
    // Invoking this method without a completedBlock is pointless
    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead"); 
}
  1. 我们可以单独使用 SDWebImageDownloader 来下载图片,但是图片内容不会缓存,因为缓存的逻辑是在SDWebImageManager中处理的
 SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    [downloader downloadImageWithURL:imageURL
                             options:0
                            progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                                // progression tracking code
                            }
                           completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
                                if (image && finished) {
                                    // do something with image
                                }
                            }];

gif的加载

4.0版本之后,SDWebImage依赖 FLAnimatedImage进行了gif的加载,需要我们单独导入pod 'SDWebImage/GIF',并且需要使用FLAnimatedImageView 代替 UIImageView,如果继续使用UIImageView来显示GIF图,将只会显示第一帧。
gif的加载主要是FLAnimatedImage 、FLAnimatedImageView 、FLAnimatedImageView+WebCache这3个类来处理GIF图片;动态图片的数据通过 ALAnimatedImage对象来封装。FLAnimatedImageView是UIImageView的子类。

SDWebImage4. x之后UIImageView默认 不启用完整GIF解码/编码。因为它会降低FLAnimatedImageView的性能。不过您可以启用UIImageView实例通过添加内置的GIF编码器

[[SDWebImageCodersManager sharedInstance] addCoder:[SDWebImageGIFCoder sharedCoder]];
@interface FLAnimatedImageView : UIImageView

@property (nonatomic, strong) FLAnimatedImage *animatedImage;// 动态图片的封装对象
@property (nonatomic, copy) void(^loopCompletionBlock)(NSUInteger loopCountRemaining);
@property (nonatomic, strong, readonly) UIImage *currentFrame;//当前动画帧对应的UIImage对象
@property (nonatomic, assign, readonly) NSUInteger currentFrameIndex;//当前图片镇对应的索引
//指定动态图片执行所在的runloop的mode。NSRunLoopCommonMode
@property (nonatomic, copy) NSString *runLoopMode;
@end
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock {

[self sd_internalSetImageWithURL:url
                    placeholderImage:placeholder
                             options:options
                        operationKey:nil
                       setImageBlock:^(UIImage *image, NSData *imageData) {

//根据NSData的类型获取图片的类型
BOOL isGIF = (image.sd_imageFormat == SDImageFormatGIF || [NSData sd_imageFormatForImageData:imageData] == SDImageFormatGIF);

if (!isGIF || isPlaceholder) {//不是动态图片,则正常显示
                               strongSelf.image = image;
                               strongSelf.animatedImage = nil;
                               dispatch_group_leave(group);
                               return;
                           }

FLAnimatedImage *animatedImage = SDWebImageCreateFLAnimatedImage(sstrongSelf, gifData);
sstrongSelf.image = animatedImage.posterImage;
 sstrongSelf.animatedImage = animatedImage;
}
}

SDWebImage3.x与SDWebImage4.x主要的改动

  • 新增的类
    SDImageCacheConfig
    SDWebImageDownloadToken

  • UIView (WebCache)类新增方法

    • sd_imageURL
    • sd_internalSetImageWithURL:placeholderImage:options:operationKey:setImageBlock:progress:completed:
    • sd_cancelCurrentImageLoad
    • sd_showActivityIndicatorView
    • sd_addActivityIndicator
    • sd_removeActivityIndicator
  • SDImageFormat 枚举 新增类型 (jpeg, png, gif, tiff, webp)
  • FLAnimatedImageView (WebCache) category for FLAnimatedImageView from FLAnimatedImage
  • 重命名的方法
    • setShowActivityIndicatorView: renamed to sd_setShowActivityIndicatorView:
    • setIndicatorStyle: renamed to sd_setIndicatorStyle:
    • renamed downloadImageWithURL:options:progress:completed to loadImageWithURL:options:progress:completed
  • renamed SDWebImageCompletionBlock to SDExternalCompletionBlock
  • renamed SDWebImageCompletionWithFinishedBlock to SDInternalCompletionBlock and added extra NSData param
  • renamed queryDiskCacheForKey:done: to queryCacheOperationForKey:done:
  • 被废弃的方法
  • removed the synchronous method diskImageExistsWithKey:
  • removed the synchronous clearDisk and deleteOldFiles
  • removed removeImageForKey: and removeImageForKey:fromDisk:
  • removed the deprecated method contentTypeForImageData:
  • removed deprecated methods:
    • imageURL
    • setImageWithURL:
    • setImageWithURL:placeholderImage:
    • setImageWithURL:placeholderImage:options:
    • setImageWithURL:completed:
    • setImageWithURL:placeholderImage:completed:
    • setImageWithURL:placeholderImage:options:completed:
    • setImageWithURL:placeholderImage:options:progress:completed:
    • sd_setImageWithPreviousCachedImageWithURL:andPlaceholderImage:options:progress:completed:
    • setAnimationImagesWithURLs:
    • cancelCurrentArrayLoad
    • cancelCurrentImageLoad
    • cachedImageExistsForURL:
    • diskImageExistsForURL:
    • SDWebImageCompletedBlock
    • SDWebImageCompletedWithFinishedBlock
    • downloadWithURL:options:progress:completed:

关于SDWebImage源码常见问题
SDWebImage4.0源码探究
SDWebImage 源码解析
SDWebImage源码阅读(上)
中文说明文档
下载器
gif
SDWebImage源码解析
SDWebImage源码解读

你可能感兴趣的:(SDWebImage的技术分享)