一、 SDWebImage 简介
This library provides an async image downloader with cache support. For convenience, we added categories for UI elements like UIImageView , UIButton , MKAnnotationView .
本文基于SDWebImage的README及阅读源码后写成,主要是使用层次上的说明,包括一些可用的参数的说明
-
SDWebImage是Github上面一个流行的Objective-C 图片下载框架,具有以下特性:
Categories for UIImageView, UIButton, MKAnnotationView adding web image and cache management: 扩展了常用的UI图片类,使得加载图片和缓存变成一行代码
An asynchronous image downloader:提供了一个异步的图片下载器
An asynchronous memory + disk image caching with automatic cache expiration handling: 一个异步的缓存机制,该机制同时利用了内存和硬盘, 并且会自动处理过期的缓存
A background image decompression: 后台图片压缩
Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.A guarantee that the same URL won't be downloaded several times 保证同一URL对应的图片不会被加载多次(内部保存了一个Dictionary,已存在的不会再次下载)
A guarantee that bogus URLs won't be retried again and again 保证了无效的URL不会一次又一次的尝试下载
A guarantee that main thread will never be blocked 主线程永不阻塞
Performances!
Use GCD and ARC
-
How is SDWebImage better than NSURLRequest? (Reference: How is SDWebImage better than NSURLRequest?
- NSURLCache需要从硬盘中读出来放到内存(硬盘中也有,但是是decode过的数据)
- Decompress的过程被强制在后台进程运行,提高UI的流畅性
- SDWebImage让你完全不用关心复杂且易错的HTTP缓存控制,使得缓存的读取更加的快
二、 SDWebImage使用
-
基本使用
#import <SDWebImage/UIImageView+WebCache.h> [imageView sd_setImageWithURL:[NSURL URLWithString: @"http://www.domain.com/path/to/image.jpg" ] placeholderImage:[UIImage imageNamed: @"placeholder.png" ]];
支持GIF格式
导入 FLAnimatedImage 模块依赖
使用FLAnimatedImageView代替UIImageView-
图片的更新
若图片的URL没有变化,SDWebImage不会更新图片,会使用缓存
若需要更新图片,有两种解决方案- 图片更新时URL应当更新
- 若你无法更改服务器的URL,可以使用SDWebImageRefreshCached选项,会降低performance, 但是会更新图片
-
部分可选参数说明(详见 SDWebImageManager.h)
-
SDWebImageRetryFailed 失败后是否重试
- By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
-
SDWebImageLowPriority 图片下载是否是低优先级的,若为YES,推迟图片的下载直到必须显示的时候
- By default, image downloads are started during UI interactions, this flags disable this feature leading to delayed download on UIScrollView deceleration for instance.
SDWebImageCacheMemoryOnly 是否仅在内存在进行缓存
SDWebImageProgressiveDownload 是否需要显示下载进度 Default:NO
-
SDWebImageRefreshCached 是否允许更新缓存的图片,参见第三点的使用。默认为NO,只有无法更改服务器的URL又需要更新图片时才使用
- Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed.
- The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.
- This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
- If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.
- Use this flag only if you can't make your URLs static with embedded cache busting parameter.
SDWebImageContinueInBackground 是否即使在后台运行也继续下载图片
SDWebImageHandleCookies WebImage的下载是否应处理Cookies,默认为YES
SDWebImageAllowInvalidSSLCertificates 是否允许不可信的SSL连接
SDWebImageHighPriority 是否将新的图片放在下载队列的首部 (默认是FIFO,若为YES,则为LIFO)
SDWebImageDelayPlaceholder 推迟placeHolder图片的加载直到图片加载完成
SDWebImageTransformAnimatedImage 执行动态图的转换
SDWebImageAvoidAutoSetImage 是否要在图片加载后手动设置图片到UIView中,默认为NO,除非你确实需要手动将图片设置到UIView中
SDWebImageScaleDownLargeImages 解压图片时是否要降低图片的分辨率
-
三、 SDWebImage 源码
- SDWebImage项目构成
-
SDWebImage
-
Downloader
- SDWebImageDownloader.h 下载器核心类,定义了核心方法和核心配置,通过单例获取实例,下载图片
-
核心方法
- ( nullable SDWebImageDownloadToken *)downloadImageWithURL:( nullable NSURL *)url options:( SDWebImageDownloaderOptions )options progress:( nullable SDWebImageDownloaderProgressBlock )progressBlock completed:( nullable SDWebImageDownloaderCompletedBlock )completedBlock;
核心配置
-
- SDWebImageDownloader.h 下载器核心类,定义了核心方法和核心配置,通过单例获取实例,下载图片
SDWebImageDownloaderOperation
-
-
Cache
- SDImageCache 缓存核心类
- SDImageCacheConfig 缓存配置文件
-
Utils
- SDWebImageManager 框架核心类,包含了Downloader,Prefetcher, Cache
- SDWebImageDecoder
- SDWebImagePrefetcher
-
Categories 辅助方法
- WebCache Categories 包装类,使得使用UIView图片类更加容易
- FLAnimatedImage 对GIF动图的支持
-
- 部分核心方法剖析
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
forURL:(nullable NSURL *)url
createCallback:(SDWebImageDownloaderOperation *(^)())createCallback {
// The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
if (url == nil) {
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return nil;
}
__block SDWebImageDownloadToken *token = nil;
// barrierQueue的原型 @property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t barrierQueue;
// This queue is used to serialize the handling of the network responses of all the download operation in a single queue
// 不同于执行网络请求,这个操作是用于处理接收到的来自本机的网络请求的,一个个为它们生成SDWebImageDownloadToken,加入了之后它们其实是异步执行的
dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
//重复的URL请求不会被加入
if (!operation) {
//创建回调方法,其实就是产生了一个operation,加进异步请求的NSOperation的队列
operation = createCallback();
self.URLOperations[url] = operation;
__weak SDWebImageDownloaderOperation *woperation = operation;
operation.completionBlock = ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
};
}
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
token = [SDWebImageDownloadToken new];
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
});
return token;
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
__weak SDWebImageDownloader *wself = self;
//调用另一个方法,这个方法主要是写了createCallBlock(这是一个返回SDWebImageDownloaderOperation* 没有参数的方法块,用于创建一个下载的操作(请求),加入队列中异步执行)
return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
//把weakself 赋值给strongself
__strong __typeof (wself) sself = wself;
NSTimeInterval timeoutInterval = sself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise(原注释)
// 只采用一个缓存机制 NSCache或SDImageCache
NSURLRequestCachePolicy cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
if (options & SDWebImageDownloaderUseNSURLCache) {
if (options & SDWebImageDownloaderIgnoreCachedResponse) {
cachePolicy = NSURLRequestReturnCacheDataDontLoad;
} else {
cachePolicy = NSURLRequestUseProtocolCachePolicy;
}
}
// 创建一个Request请求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:cachePolicy timeoutInterval:timeoutInterval];
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
request.HTTPShouldUsePipelining = YES;
if (sself.headersFilter) {
request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = sself.HTTPHeaders;
}
//创建一个NSOperation, 一个NSOperation负责一个图片的加载
SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
operation.shouldDecompressImages = sself.shouldDecompressImages;
if (sself.urlCredential) {
operation.credential = sself.urlCredential;
} else if (sself.username && sself.password) {
operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
}
if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
}
// 把operation加入NSOperationQueue中,这个Queue是异步的
[sself.downloadQueue addOperation:operation];
if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
[sself.lastAddedOperation addDependency:operation];
sself.lastAddedOperation = operation;
}
return operation;
}];
}
四、 其他
- 单例方法均采用了 dispatch_once, 既保证了线程安全性,也保证了效率
+ (nonnull instancetype)sharedDownloader {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
- 向系统注册通知,接收到内存不足或进入后台的通知时进行相应的处理
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(clearMemory)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deleteOldFiles)
name:UIApplicationWillTerminateNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundDeleteOldFiles)
name:UIApplicationDidEnterBackgroundNotification
object:nil];