1、 SDWebImageOptions 枚举值意义
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.
* 默认情况下,当一个 URL 下载失败,该URL被列入黑名单,将不会继续尝试下载
* This flag disable this blacklisting.
* 此标志取消黑名单
*/
SDWebImageRetryFailed = 1 << 0,
/**
* By default, image downloads are started during UI interactions, this flags disable this feature,
* 默认情况下,在 UI 交互时也会启动图像下载,此标记取消这一功能
* leading to delayed download on UIScrollView deceleration for instance.
* 会延迟下载,UIScrollView停止滚动之后再继续下载
* 下载事件监听的运行循环模式是 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.
* 即使图像被缓存,遵守 HTPP 响应的缓存控制,如果需要,从远程刷新图像
* The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.
* 磁盘缓存将由 NSURLCache 处理,而不是 SDWebImage,这会对性能有轻微的影响
* This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
* 此选项有助于处理同一个请求 URL 的图像发生变化
* If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.
* 如果缓存的图像被刷新,会调用一次 completion block,并传递最终的图像
*
* Use this flag only if you can't make your URLs static with embedded cache busting parameter.
* 仅在无法使用嵌入式缓存清理参数确定图像 URL 时,使用此标记
*/
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
* 在 iOS 4+,当 App 进入后台后仍然会继续下载图像。这是向系统请求额外的后台时间以保证下载请求完成的
* extra time in background to let the request finish. If the background task expires the operation will be cancelled.
* 如果后台任务过期,请求将会被取消
*/
SDWebImageContinueInBackground = 1 << 5,
/**
* Handles cookies stored in NSHTTPCookieStore by setting
* 通过设置
* NSMutableURLRequest.HTTPShouldHandleCookies = YES;
* 处理保存在 NSHTTPCookieStore 中的 cookies
*/
SDWebImageHandleCookies = 1 << 6,
/**
* Enable to allow untrusted SSL certificates.
* 允许不信任的 SSL 证书
* Useful for testing purposes. Use with caution in production.
* 可以出于测试目的使用,在正式产品中慎用
*/
SDWebImageAllowInvalidSSLCertificates = 1 << 7,
/**
* By default, images are loaded in the order in which they were queued. This flag moves them to
* 默认情况下,图像会按照在队列中的顺序被加载,此标记会将它们移动到队列前部立即被加载
* the front of the queue.
*/
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,
* 通常不会在可动画的图像上调用 transformDownloadedImage 代理方法,因为大多数转换代码会破坏动画文件
* as most transformation code would mangle it.
* Use this flag to transform them anyway.
* 使用此标记尝试转换
*/
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
*/
SDWebImageAvoidAutoSetImage = 1 << 11 // 避免自动设置图像
};
2、清理磁盘
// 清理磁盘
- (void)cleanDisk {
[self cleanDiskWithCompletionBlock:nil];
}
- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock {
dispatch_async(self.ioQueue, ^{
// 1. 找磁盘缓存目录
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
/* 2.
NSURLIsDirectoryKey, 目录key
NSURLContentModificationDateKey, url指向的内容,最后修改时间的key
NSURLTotalFileAllocatedSizeKey 文件总大小的key
*/
NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
// 3.Enumerator(链表的数据结构存在的) 迭代器的设计模式
// 创建目录文件的枚举器
/*
NSDirectoryEnumerationSkipsHiddenFiles 不遍历隐藏文件
NSDirectoryEnumerationSkipsSubdirectoryDescendants 不要下载到目录中
NSDirectoryEnumerationSkipsPackageDescendants 不要下到包
*/
NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
// 4.从现在开始,减去最大时间
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
// 5.定义了一个字典
NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
// 6.定义了当前缓存的大小
NSUInteger currentCacheSize = 0;
// Enumerate all of the files in the cache directory. This loop has two purposes:
//
// 1. Removing files that are older than the expiration date.
// 2. Storing file attributes for the size-based cleanup pass.
// 7. 创建一个可变数组
NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
// 8.拿到具体的URL
/*
1.清楚超过过期时间的文件
2.以大小为基础进行第二轮文件保存
*/
for (NSURL *fileURL in fileEnumerator) {
// 9.获取指定的key的信息, 以字典的方式返回
NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];
// Skip directories.
// 10. 判断这个目录,有就直接跳过
if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
}
// Remove files that are older than the expiration date;
// 11.拿到文件的修改日期
NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
// 12.判断是不是最后修改,是最后修改,就添加到数组
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
}
// Store a reference to this file and account for its total size.
// 14.对未过期的文件, 以键值的形式存起来,方便后面重置缓存
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
[cacheFiles setObject:resourceValues forKey:fileURL];
}
// 15. 直接删除
for (NSURL *fileURL in urlsToDelete) {
[_fileManager removeItemAtURL:fileURL error:nil];
}
// If our remaining disk cache exceeds a configured maximum size, perform a second
// size-based cleanup pass. We delete the oldest files first.
// 有一个上限,如果自己设置了上限,才会走if分支
if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
// Target half of our maximum cache size for this cleanup pass.
// 16. 所需的缓存大小一般的时候
const NSUInteger desiredCacheSize = self.maxCacheSize / 2;
// Sort the remaining cache files by their last modification time (oldest first).
// 把上次缓存的文件进行排序, 最早进来的排第一
/*NSSortConcurrent 并行的方式
*/
NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
}];
// Delete files until we fall below our desired cache size.
// 遍历删除文件.直到低于缓存大小
for (NSURL *fileURL in sortedFiles) {
if ([_fileManager removeItemAtURL:fileURL error:nil]) {
NSDictionary *resourceValues = cacheFiles[fileURL];
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];
if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
}
有意思的宏
#define dispatch_main_sync_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_sync(dispatch_get_main_queue(), block);\
}
// 安全的主线程操作,if判断很正确
#define dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}