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 框架的结构划分
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