SDWebImage初探

记录一下SDWebImage第三方源码的研究

流程图

流程图

流程说明

1
2
3
4
5
首先会在 SDWebImageCache 中寻找图片是否有对应的缓存, 它会以url 作为数据的索引先在内存中寻找是否有对应的缓存
如果缓存未找到就会利用通过MD5处理过的key来继续在磁盘中查询对应的数据, 如果找到了, 就会把磁盘中的数据加载到内存中,并将图片显示出来
如果在内存和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片
下载后的图片会加入缓存中,并写入磁盘中
整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来

伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#pragma mark - 检查图片缓存
//从内存缓存中取出
if(有图片)
{
//直接设置
}else{
#pragma mark - 检查磁盘缓存
//获取文件路径

//得到图片的名称 获取节点

//拼接文件的全路径

//从磁盘缓存中取出

if(data)
{ //将二进制数去转成图片

//设置图片

//把图片存到内存缓存中

}else{
#pragma mark - 检查操作缓存
//清空图片或者是设置占位图片

//检查操作缓存

if (操作缓存) {
//如果存在,那么什么都不做
}else{
//操作步骤
{
//下载图片三步骤
if (图片为空) {
//移除操作缓存中的操作

//直接返回
return ;
}
//把图片存到内存缓存中

//把图片保存到磁盘缓存

//回到主线程设置图片

}

//添加操作到缓存中

//将操作加入队列

}
}
#pragma mark - 移除缓存策略
//移除内存缓存

//取消队列中的操作


下载图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//下载图片的核心方法
/*
* url 图片的二进制数据
* placeholder UIImageView的占位图片
* options 图片下载选项(策略)
* progressBlock 进度回调
* completedBlock 完成回调
*/
- (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 <SDWebImageOperation> 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);
}
});
}
}

下载操作队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
* 如果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 <SDWebImageOperation>)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 <SDWebImageOperation> 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;
}

缓存及磁盘操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//检查要下载图片的缓存情况
/*
1.先检查是否有内存缓存
2.如果没有内存缓存则检查是否有沙盒缓存
3.如果有沙盒缓存,则对该图片进行内存缓存处理并执行doneBlock回调
*/
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {

//如果回调不存在,则直接返回
if (!doneBlock) {
return nil;
}

//如果缓存对应的key为空,则直接返回,并把存储方式(无缓存)通过block块以参数的形式传递
if (!key) {
doneBlock(nil, SDImageCacheTypeNone);
return nil;
}

// First check the in-memory cache...
//检查该KEY对应的内存缓存,如果存在内存缓存,则直接返回,并把图片和存储方式(内存缓存)通过block块以参数的形式传递
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
doneBlock(image, SDImageCacheTypeMemory);
return nil;
}

//创建一个操作
NSOperation *operation = [NSOperation new];
//使用异步函数,添加任务到串行队列中(会开启一个子线程处理block块中的任务)
dispatch_async(self.ioQueue, ^{
//如果当前的操作被取消,则直接返回
if (operation.isCancelled) {
return;
}

@autoreleasepool {
//检查该KEY对应的磁盘缓存
UIImage *diskImage = [self diskImageForKey:key];
//如果存在磁盘缓存,且应该把该图片保存一份到内存缓存中,则先计算该图片的cost(成本)并把该图片保存到内存缓存中
if (diskImage && self.shouldCacheImagesInMemory) {
//计算图片的Cost
NSUInteger cost = SDCacheCostForImage(diskImage);
//对该图片进行内存缓存处理
[self.memCache setObject:diskImage forKey:key cost:cost];
}

//线程间通信,在主线程中回调doneBlock,并把图片和存储方式(磁盘缓存)通过block块以参数的形式传递
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, SDImageCacheTypeDisk);
});
}
});

return operation;
}

注:(磁盘缓存目录)

缓存在磁盘沙盒目录下 Library/Caches
二级目录为 ~/Library/Caches/default/com.hackemist.SDWebImageCache.default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#pragma mark SDImageCache Life Cycle

//初始化方法,默认的缓存空间名称为default
- (id)init {
return [self initWithNamespace:@"default"];
}

//使用指定的命名空间实例化一个新的缓存存储
- (id)initWithNamespace:(NSString *)ns {
//根据传入的命名空间设置磁盘缓存路径
NSString *path = [self makeDiskCachePath:ns];
return [self initWithNamespace:ns diskCacheDirectory:path];
}

//使用指定的命名空间实例化一个新的缓存存储和目录
//拼接完成的结果为:沙盒--》caches路径--》default--》com.hackemist.SDWebImageCache.default
- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory {
if ((self = [super init])) {

//拼接默认的磁盘缓存目录
NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];

// initialise PNG signature data
// 初始化PNG数据签名 8字节
kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8];

// Create IO serial queue
// 创建处理IO操作的串行队列
_ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);

// Init default values
// 初始化默认的最大缓存时间 == 1周
_maxCacheAge = kDefaultCacheMaxCacheAge;

// Init the memory cache
// 初始化内存缓存,使用NSCache(AutoPurgeCache)
_memCache = [[AutoPurgeCache alloc] init];

//设置默认的缓存磁盘目录
_memCache.name = fullNamespace;

// Init the disk cache
//初始化磁盘缓存,如果磁盘缓存路径不存在则设置为默认值,否则根据命名空间重新设置
if (directory != nil) {
//以默认值得方式拼接
_diskCachePath = [directory stringByAppendingPathComponent:fullNamespace];
} else {
//根据命名空间重新设置
NSString *path = [self makeDiskCachePath:ns];
_diskCachePath = path;
}

// Set decompression to YES
// 设置图片是否解压缩,默认为YES
_shouldDecompressImages = YES;

// memory cache enabled
// 是否进行内存缓存(默认为YES)
_shouldCacheImagesInMemory = YES;

// Disable iCloud
// 是否禁用iCloud备份,默认为YES
_shouldDisableiCloud = YES;

//同步函数+串行队列:在当前线程中同步的初始化文件管理者
dispatch_sync(_ioQueue, ^{
_fileManager = [NSFileManager new];
});

#if TARGET_OS_IOS
// Subscribe to app events
//监听应用程序通知
//当监听到UIApplicationDidReceiveMemoryWarningNotification(系统级内存警告)调用clearMemory方法
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(clearMemory)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];

//当监听到UIApplicationWillTerminateNotification(程序将终止)调用cleanDisk方法
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(cleanDisk)
name:UIApplicationWillTerminateNotification
object:nil];

//当监听到UIApplicationDidEnterBackgroundNotification(进入后台),调用backgroundCleanDisk方法
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundCleanDisk)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
#endif
}

return self;
}

遇到过的两种需求

一、图片进过加密处理,一段时间内可以访问,即图片地址后面跟的参数不同,但加载出来的图片是同一张,为了不重复下载,如何过滤图片地址?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
方法: 写在viewDidLoad 或者 appDelegate里

1.版本5.0之前
// URL过滤器
[[SDWebImageManager sharedManager] setCacheKeyFilter:^NSString * _Nullable(NSURL * _Nullable url) {
// 去除参数 ?后面的
url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
return [url relativeString];
}];

2.版本5.0之后
// URL过滤器
SDWebImageCacheKeyFilter *filter = [[SDWebImageCacheKeyFilter alloc] initWithBlock:^NSString * _Nullable(NSURL * _Nonnull url) {
// 去除参数 ?后面的
url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
return [url relativeString];
}];
[[SDWebImageManager sharedManager] setCacheKeyFilter:filter];

二、如何做到tableView滚动时 不加载cell中的图片,停止滚动或减速时才加图片?

需要用到的核心方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
 * 根据图片的url下载图片并设置到ImageView上面去,占位图片
* 异步下载并缓存
*
* @param url 图片的URL
* @param placeholder 显示在UIImageView上面的占位图片,直到图片下载完成
* @param options 下载图片的选项。参考SDWebImageOptions的枚举值
* @param completedBlock 当操作执行完毕之后的回调。该回调没有返回值
* 第一个参数为请求的图片
* 第二个参数是NSError类型的,如果图片下载成功则error为nil,否则error有值
* 第三个参数是图片缓存的使用情况(内存缓存|沙盒缓存|直接下载)
* 第四个参数是图片的URL地址
*/
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock;

SDWebImageOptions 枚举类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//使用位移枚举,通过按位与&按位或|的组合方式传递多个值
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
/**
* By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
* This flag disable this blacklisting.
*
* 默认情况下,如果一个url在下载的时候失败了,那么这个url会被加入黑名单,不会尝试再次下载。如果使用该参数,则该URL不会被添加到黑名单中。意味着会对下载失败的URL尝试重新下载。
* 此标记取消黑名单
*/
SDWebImageRetryFailed = 1 << 0, //失败后尝试重新下载

/**
* By default, image downloads are started during UI interactions, this flags disable this feature,
* leading to delayed download on UIScrollView deceleration for instance.
*
* 默认情况下,在 UI 交互时也会启动图像下载,此标记取消这一特性
* 会推迟到滚动视图停止滚动之后再继续下载
* 备注:NSURLConnection 的网络下载事件监听的运行循环模式是 NSDefaultRunLoopMode
*/
SDWebImageLowPriority = 1 << 1,//低优先级

/**
* This flag disables on-disk caching
*
* 使用该参数,将禁止磁盘缓存,只做内存缓存
*/
SDWebImageCacheMemoryOnly = 1 << 2,//只使用内存缓存

/**
* This flag enables progressive download, the image is displayed progressively during download as a browser would do.
* By default, the image is only displayed once completely downloaded.
*
* 此标记允许渐进式下载,就像浏览器中那样,下载过程中,图像会逐步显示出来
* 默认情况下,图像会在下载完成后一次性显示
*/
SDWebImageProgressiveDownload = 1 << 3,//渐进式下载

/**
* 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.
*
* 遵守 HTPP 响应的缓存控制,如果需要,从远程刷新图像
* 磁盘缓存将由 NSURLCache 处理,而不是 SDWebImage,这会对性能有轻微的影响
* 此选项用于处理URL指向图片发生变化的情况
* 如果缓存的图像被刷新,会调用一次 completion block,并传递最终的图像
*/
SDWebImageRefreshCached = 1 << 4, //刷新缓存

/**
* In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for
* extra time in background to let the request finish. If the background task expires the operation will be cancelled.
*
* 如果系统版本是iOS 4+的,那么当App进入后台后仍然会继续下载图像。
* 这是向系统请求额外的后台时间以保证下载请求完成的
* 如果后台任务过期,请求将会被取消
*/
SDWebImageContinueInBackground = 1 << 5, //后台下载

/**
* Handles cookies stored in NSHTTPCookieStore by setting
* NSMutableURLRequest.HTTPShouldHandleCookies = YES;
*
* 通过设置,处理保存在 NSHTTPCookieStore 中的 cookies
*/
SDWebImageHandleCookies = 1 << 6, //处理保存在NSHTTPCookieStore中的cookies

/**
* Enable to allow untrusted SSL certificates.
* Useful for testing purposes. Use with caution in production.
*
* 允许不信任的 SSL 证书
* 可以出于测试目的使用,在正式产品中慎用
*/
SDWebImageAllowInvalidSSLCertificates = 1 << 7, //允许不信任的 SSL 证书

/**
* By default, image are loaded in the order they were queued. This flag move them to
* the front of the queue and is loaded immediately instead of waiting for the current queue to be loaded (which
* could take a while).
*
* 默认情况下,图像会按照添加到队列中的顺序被加载,此标记会将它们移动到队列前端被立即加载
* 而不是等待当前队列被加载,因为等待队列加载会需要一段时间
*/
SDWebImageHighPriority = 1 << 8, //高优先级(优先下载)

/**
* By default, placeholder images are loaded while the image is loading. This flag will delay the loading
* of the placeholder image until after the image has finished loading.
*
* 默认情况下,在加载图像时,占位图像已经会被加载。
* 此标记会延迟加载占位图像,直到图像已经完成加载
*/
SDWebImageDelayPlaceholder = 1 << 9, //延迟占位图片

/**
* We usually don't call transformDownloadedImage delegate method on animated images,
* as most transformation code would mangle it.
* Use this flag to transform them anyway.
*
* 通常不会在可动画的图像上调用transformDownloadedImage代理方法,因为大多数转换代码会破坏动画文件
* 使用此标记尝试转换
*/
SDWebImageTransformAnimatedImage = 1 << 10, //转换动画图像

/**
* By default, image is added to the imageView after download. But in some cases, we want to
* have the hand before setting the image (apply a filter or add it with cross-fade animation for instance)
* Use this flag if you want to manually set the image in the completion when success
*
* 下载完成后手动设置图片,默认是下载完成后自动放到ImageView上
*/
SDWebImageAvoidAutoSetImage = 1 << 11 //手动设置图像
};

参数填成以下就可以了

1
options: SDWebImageRetryFailed |SDWebImageLowPriority