SDWebImage之SDImageCache
SDImageCache和SDWebImageDownloader是SDWebImage库的最重要的两个部件,它们一起为SDWebImageManager提供服务,来完成图片的加载。SDImageCache提供了对图片的内存缓存、异步磁盘缓存、图片缓存查询等功能,下载过的图片会被缓存到内存,也可选择保存到本地磁盘,当再次请求相同图片时直接从缓存中读取图片,从而大大提高了加载速度。在SDImageCache中,内存缓存是通过 NSCache的子类
AutoPurgeCache来实现的;磁盘缓存是通过 NSFileManager
来实现文件的存储(默认路径为/Library/Caches/default/com.hackemist.SDWebImageCache.default),是异步实现的。
下面我们还是一步步来分析它的源码结构。
1.SDImageCacheType枚举类型
/**
缓存类型
*/
typedef NS_ENUM(NSInteger, SDImageCacheType) {
/**
* The image wasn't available the SDWebImage caches, but was downloaded from the web.
*/
SDImageCacheTypeNone, //不使用 SDWebImage 缓存,从网络下载
/**
* The image was obtained from the disk cache.
*/
SDImageCacheTypeDisk, //使用磁盘缓存
/**
* The image was obtained from the memory cache.
*/
SDImageCacheTypeMemory //使用内存缓存
};
2.Block定义
/**
查询回调Block @param image 图片
@param data 图片数据
@param cacheType 缓存类型
*/
typedef void(^SDCacheQueryCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType); /**
磁盘缓存检查回调 @param isInCache 是否在缓存中
*/
typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache); /**
磁盘缓存空间大小计算回调Block @param fileCount 文件数量
@param totalSize 总大小
*/
typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);
3.SDImageCache的属性
//SDImageCacheConfig.h
@property (assign, nonatomic) BOOL shouldDecompressImages; //!<是否在缓存之前解压图片,此项操作可以提升性能,但是会消耗较多的内存,默认是YES。注意:如果内存不足,可以置为NO
@property (assign, nonatomic) BOOL shouldDisableiCloud; //!<是否禁止iCloud备份,默认是YES
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory; //!<是否启用内存缓存 默认是YES
@property (assign, nonatomic) NSInteger maxCacheAge; //!<磁盘缓存保留的最长时间,默认为1周
@property (assign, nonatomic) NSUInteger maxCacheSize; //!<磁盘缓存最大容量,以字节为单位,默认为0,表示不做限制 //SDImageCache.h
@property (nonatomic, nonnull, readonly) SDImageCacheConfig *config; //!<SDImageCacheConfig
@property (assign, nonatomic) NSUInteger maxMemoryCost; //!<内存最大容量
@property (assign, nonatomic) NSUInteger maxMemoryCountLimit; //!<缓存中可以存放缓存的最大数量,与NSCache相关 //SDImageCache.m
@property (strong, nonatomic, nonnull) NSCache *memCache; //!<内存缓存
@property (strong, nonatomic, nonnull) NSString *diskCachePath; //!<磁盘缓存路径
@property (strong, nonatomic, nullable) NSMutableArray<NSString *> *customPaths; //!<自定义的缓存路径
@property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t ioQueue; //!<磁盘缓存操作的串行队列
4.SDImageCache的方法
从源码表面来看,SDImageCache提供的方法很多,但其方法主要围绕着图片缓存的添加、删除、查询等,只不过每个功能可能有多种实现方式,但本质上是一样的。
4.1初始化
/**
单例方法,返回一个全局的缓存实例 @return 缓存实例
*/
+ (nonnull instancetype)sharedImageCache {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
} - (instancetype)init {
return [self initWithNamespace:@"default"];
} /**
使用指定的命名空间实例化一个新的缓存存储 @param ns 命名空间
@return 缓存实例
*/
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns {
NSString *path = [self makeDiskCachePath:ns];
return [self initWithNamespace:ns diskCacheDirectory:path];
} /**
使用指定的命名空间实例化一个新的缓存存储和目录 @param ns 命名空间
@param directory 缓存所在目录
@return 缓存实例
*/
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory {
if ((self = [super init])) {
NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns]; // Create IO serial queue
_ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
//初始化缓存策略配置对象
_config = [[SDImageCacheConfig alloc] init]; // Init the memory cache
// 初始化内存缓存对象
_memCache = [[AutoPurgeCache alloc] init];
_memCache.name = fullNamespace; // 初始化磁盘缓存路径
if (directory != nil) {
_diskCachePath = [directory stringByAppendingPathComponent:fullNamespace];
} else {
NSString *path = [self makeDiskCachePath:ns];
_diskCachePath = path;
} dispatch_sync(_ioQueue, ^{
_fileManager = [NSFileManager new];
}); #if SD_UIKIT
// Subscribe to app events
//当应用收到内存警告的时候,清除内存缓存
[[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];
#endif
} return self;
}
上面就是SDImageCache的初始化过程,主要是初始化内存缓存对象和初始化磁盘缓存目录,以及对一些默认参数的赋值。
4.2缓存图片
/**
以key为键值将图片image存储到缓存中 @param image 图片
@param key key
@param completionBlock 回调Block
*/
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
[self storeImage:image imageData:nil forKey:key toDisk:YES completion:completionBlock];
} /**
以key为键值将图片image存储到缓存中 @param image 图片
@param key key
@param toDisk 是否写入磁盘缓存
@param completionBlock 回调Block
*/
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
[self storeImage:image imageData:nil forKey:key toDisk:toDisk completion:completionBlock];
} /**
把一张图片存入缓存的具体实现 @param image 缓存的图片对象
@param imageData 缓存的图片数据
@param key 缓存对应的key
@param toDisk 是否缓存到磁盘
@param completionBlock 缓存完成回调
*/
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
if (!image || !key) {
if (completionBlock) {
completionBlock();
}
return;
}
// if memory cache is enabled
//缓存到内存
if (self.config.shouldCacheImagesInMemory) {
//计算缓存数据的大小
NSUInteger cost = SDCacheCostForImage(image);
//加入缓存
[self.memCache setObject:image forKey:key cost:cost];
} if (toDisk) {
//在一个串行队列中做磁盘缓存操作
dispatch_async(self.ioQueue, ^{
@autoreleasepool {
NSData *data = imageData;
if (!data && image) {
//获取图片的类型GIF/PNG等
SDImageFormat imageFormatFromData = [NSData sd_imageFormatForImageData:data];
//根据指定的SDImageFormat,把图片转换为对应的data数据
data = [image sd_imageDataAsFormat:imageFormatFromData];
}
//把处理好了的数据存入磁盘
[self storeImageDataToDisk:data forKey:key];
} if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
} else {
if (completionBlock) {
completionBlock();
}
}
} /**
把图片资源存入磁盘 @param imageData 图片数据
@param key key
*/
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
if (!imageData || !key) {
return;
} [self checkIfQueueIsIOQueue];
//缓存目录是否已经初始化
if (![_fileManager fileExistsAtPath:_diskCachePath]) {
[_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
} // get cache Path for image key
//获取key对应的完整缓存路径
NSString *cachePathForKey = [self defaultCachePathForKey:key];
// transform to NSUrl
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
//把数据存入路径
[_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil]; // disable iCloud backup
if (self.config.shouldDisableiCloud) {
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}
4.3查询图片
/**
根据key判断磁盘缓存中是否存在图片 @param key key
@param completionBlock 回调Block
*/
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
dispatch_async(_ioQueue, ^{
BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]]; // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
// checking the key with and without the extension
if (!exists) {
exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key].stringByDeletingPathExtension];
} if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(exists);
});
}
});
} /**
根据key获取缓存在内存中的图片 @param key key
@return 缓存的图片
*/
- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key {
return [self.memCache objectForKey:key];
} /**
根据key获取缓存在磁盘中的图片 @param key key
@return 缓存的图片
*/
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
} return diskImage;
} /**
根据key获取缓存图片(先从内存中搜索,再去磁盘中搜索) @param key key
@return 缓存的图片
*/
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key {
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
return image;
} // Second check the disk cache...
image = [self imageFromDiskCacheForKey:key];
return image;
} /**
根据指定的key,获取存储在磁盘上的数据 @param key 图片对应的key
@return 返回图片数据
*/
- (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key {
//获取key对应的path
NSString *defaultPath = [self defaultCachePathForKey:key];
NSData *data = [NSData dataWithContentsOfFile:defaultPath];
if (data) {
return data;
} // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
// checking the key with and without the extension
//如果key没有后缀名,则会走到这里通过这里读取
data = [NSData dataWithContentsOfFile:defaultPath.stringByDeletingPathExtension];
if (data) {
return data;
}
//如果在默认路径没有找到图片,则在自定义路径迭代查找
NSArray<NSString *> *customPaths = [self.customPaths copy];
for (NSString *path in customPaths) {
NSString *filePath = [self cachePathForKey:key inPath:path];
NSData *imageData = [NSData dataWithContentsOfFile:filePath];
if (imageData) {
return imageData;
} // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
// checking the key with and without the extension
imageData = [NSData dataWithContentsOfFile:filePath.stringByDeletingPathExtension];
if (imageData) {
return imageData;
}
} return nil;
} /**
根据指定的key获取image对象 @param key key
@return image对象
*/
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key {
//获取磁盘数据
NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
if (data) {
UIImage *image = [UIImage sd_imageWithData:data];
image = [self scaledImageForKey:key image:image];
if (self.config.shouldDecompressImages) {
image = [UIImage decodedImageWithImage:image];
}
return image;
}
else {
return nil;
}
} - (nullable UIImage *)scaledImageForKey:(nullable NSString *)key image:(nullable UIImage *)image {
return SDScaledImageForKey(key, image);
} /**
在缓存中查询对应key的数据 @param key 要查询的key
@param doneBlock 查询结束以后的回调Block
@return 返回做查询操作的NSOperation
*/
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
if (!key) {
if (doneBlock) {
doneBlock(nil, nil, SDImageCacheTypeNone);
}
return nil;
} // First check the in-memory cache...
//首先从内存中查找图片
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
NSData *diskData = nil;
if ([image isGIF]) {
diskData = [self diskImageDataBySearchingAllPathsForKey:key];
}
if (doneBlock) {
doneBlock(image, diskData, SDImageCacheTypeMemory);
}
return nil;
}
//新建一个NSOperation来获取磁盘图片
NSOperation *operation = [NSOperation new];
dispatch_async(self.ioQueue, ^{
if (operation.isCancelled) {
// do not call the completion if cancelled
return;
}
//在一个自动释放池中处理图片从磁盘加载
@autoreleasepool {
NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
//把从磁盘取出的缓存图片加入内存缓存中
[self.memCache setObject:diskImage forKey:key cost:cost];
}
//图片处理完成以后回调Block
if (doneBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
});
}
}
}); return operation;
}
4.4删除图片
#pragma mark - Remove Ops /**
通过key从磁盘和内存中移除缓存 @param key key
@param completion 回调Block
*/
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion {
[self removeImageForKey:key fromDisk:YES withCompletion:completion];
} /**
移除指定key对应的缓存数据 @param key key
@param fromDisk 是否也清除磁盘缓存
@param completion 回调
*/
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion {
if (key == nil) {
return;
}
//移除内存缓存
if (self.config.shouldCacheImagesInMemory) {
[self.memCache removeObjectForKey:key];
}
//移除磁盘缓存
if (fromDisk) {
dispatch_async(self.ioQueue, ^{
[_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil]; if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
});
} else if (completion){
completion();
} } #pragma mark - Cache clean Ops 内存缓存清理相关操作 /**
清理当前SDImageCache对象的内存缓存
*/
- (void)clearMemory {
[self.memCache removeAllObjects];
} /**
移除所有的磁盘缓存图片数据 @param completion 移除完成以后回调
*/
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion {
dispatch_async(self.ioQueue, ^{
[_fileManager removeItemAtPath:self.diskCachePath error:nil];
[_fileManager createDirectoryAtPath:self.diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL]; if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
});
} - (void)deleteOldFiles {
[self deleteOldFilesWithCompletionBlock:nil];
} /**
当应用终止或者进入后台都会调用这个方法来清除缓存图片
这里会根据图片存储时间来清理图片,默认是一周,从最老的图片开始清理。如果图片缓存空间小于一个规定值,则不考虑 @param completionBlock 清除完成以后的回调
*/
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock {
dispatch_async(self.ioQueue, ^{
//获取磁盘缓存的默认根目录
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
NSArray<NSString *> *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey]; // This enumerator prefetches useful properties for our cache files.
NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL]; NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.config.maxCacheAge];
NSMutableDictionary<NSURL *, NSDictionary<NSString *, id> *> *cacheFiles = [NSMutableDictionary dictionary];
NSUInteger currentCacheSize = ; // 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.
//迭代缓存目录。有两个目的:
//1 删除比指定日期更老的图片
//2 记录文件的大小,以提供给后面删除使用
NSMutableArray<NSURL *> *urlsToDelete = [[NSMutableArray alloc] init];
for (NSURL *fileURL in fileEnumerator) {
NSError *error;
//获取指定url对应文件的指定三种属性的key和value
NSDictionary<NSString *, id> *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:&error]; // Skip directories and errors.
//如果是文件夹则返回
if (error || !resourceValues || [resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
} // Remove files that are older than the expiration date;
//获取指定url文件对应的修改日期
NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
//如果修改日期大于指定日期,则加入要移除的数组里
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
} // Store a reference to this file and account for its total size.
//获取指定的url对应的文件的大小,并且把url与对应大小存入一个字典中
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += totalAllocatedSize.unsignedIntegerValue;
cacheFiles[fileURL] = resourceValues;
}
//删除所有最后修改日期大于指定日期的所有文件
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 (self.config.maxCacheSize > && currentCacheSize > self.config.maxCacheSize) {
// Target half of our maximum cache size for this cleanup pass.
const NSUInteger desiredCacheSize = self.config.maxCacheSize / ; // Sort the remaining cache files by their last modification time (oldest first).
//根据文件创建的时间排序
NSArray<NSURL *> *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<NSString *, id> *resourceValues = cacheFiles[fileURL];
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize -= totalAllocatedSize.unsignedIntegerValue; if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
//执行完毕,主线程回调
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
} #if SD_UIKIT /**
应用进入后台的时候,调用这个方法
*/
- (void)backgroundDeleteOldFiles {
Class UIApplicationClass = NSClassFromString(@"UIApplication");
if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
return;
}
UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
//如果backgroundTask对应的时间结束了,任务还没有处理完成,则直接终止任务
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
//当任务非正常终止的时候,做清理工作
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}]; // Start the long-running task and return immediately.
//图片清理结束以后,处理完成
[self deleteOldFilesWithCompletionBlock:^{
//清理完成以后,终止任务
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
}
#endif
4.5其他方法
#pragma mark - Cache paths /**
添加只读的缓存路径 @param path 路径
*/
- (void)addReadOnlyCachePath:(nonnull NSString *)path {
if (!self.customPaths) {
self.customPaths = [NSMutableArray new];
} if (![self.customPaths containsObject:path]) {
[self.customPaths addObject:path];
}
} /**
获取指定key对应的完整缓存路径 @param key 对应一张图片,比如图片的名字
@param path 指定根目录
@return 完整路径
*/
- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path {
NSString *filename = [self cachedFileNameForKey:key];
return [path stringByAppendingPathComponent:filename];
} - (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key {
return [self cachePathForKey:key inPath:self.diskCachePath];
} /**
MD5加密 @param key key
@return 加密后的数据
*/
- (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);
NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
r[], r[], r[], r[], r[], r[], r[], r[], r[], r[], r[],
r[], r[], r[], r[], r[], [key.pathExtension isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", key.pathExtension]]; return filename;
} /**
图片缓存目录 @param fullNamespace 自定义子目录名称
@return 完整目录
*/
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace {
NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
return [paths[] stringByAppendingPathComponent:fullNamespace];
} #pragma mark - Cache Info /**
磁盘缓存使用的大小 @return 磁盘缓存的大小
*/
- (NSUInteger)getSize {
__block NSUInteger size = ;
dispatch_sync(self.ioQueue, ^{
NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath];
for (NSString *fileName in fileEnumerator) {
NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
NSDictionary<NSString *, id> *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
size += [attrs fileSize];
}
});
return size;
} /**
磁盘缓存的图片数量 @return 缓存的图片数量
*/
- (NSUInteger)getDiskCount {
__block NSUInteger count = ;
dispatch_sync(self.ioQueue, ^{
NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath];
count = fileEnumerator.allObjects.count;
});
return count;
} /**
异步计算磁盘缓存的大小 @param completionBlock 回调Block
*/
- (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock {
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES]; dispatch_async(self.ioQueue, ^{
NSUInteger fileCount = ;
NSUInteger totalSize = ; NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:@[NSFileSize]
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL]; for (NSURL *fileURL in fileEnumerator) {
NSNumber *fileSize;
[fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL];
totalSize += fileSize.unsignedIntegerValue;
fileCount += ;
} if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(fileCount, totalSize);
});
}
});
}
SDWebImage之SDImageCache的更多相关文章
- iOS 用 SDWebImage 清理图片缓存
效果图如下: 1.找到 SDWebImage找到SDImageCache类 2.添加如下方法 - (float)checkTmpSize { ; NSDirectoryEnumerator *file ...
- iOS之SDWebImage清理缓存
.找到 SDWebImage找到SDImageCache类 添加如下方法 - (float)checkTmpSize { float totalSize = 0; NSDirectoryEnumera ...
- SDWebImage源码分析
1.概述 SDWebImage是iOS开发中,被广泛使用的一个第三方开源库,提供了图片从加载.解析.处理.缓存.清理等一些列功能,让我们能够专心于业务的处理.本篇会从SDWebImage的源码,来一步 ...
- iOS开源照片浏览器框架SGPhotoBrowser的设计与实现
简介 近日在制作一个开源加密相册时附带着设计了一个照片浏览器,在进一步优化后发布到了GitHub供大家使用,该框架虽然没有MWPhotoBrowser那么强大,但是使用起来更为方便,操作更符合常规相册 ...
- SDWebImage源码解读_之SDWebImageDecoder
第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...
- SDWebImage源码解读之SDWebImageCache(上)
第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...
- SDWebImage源码解读之SDWebImageCache(下)
第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...
- 【原】SDWebImage源码阅读(五)
[原]SDWebImage源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 前面的代码并没有特意去讲SDWebImage的缓存机制,主要是想单独开一章节专门讲 ...
- 【原】SDWebImage源码阅读(四)
[原]SDWebImage源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 SDWebImage中主要实现了NSURLConnectionDataDelega ...
随机推荐
- json初接触
<html lang="en"> <head> <meta charset="UTF-8"> <meta name=& ...
- 如何创建.gitignore文件,忽略不必要提交的文件
1.gitignore 在工程实现过程中,会生成一些中间文件,或者在项目中的部分文件是不需要进行版本管理的.对于这些文件应该对于Github来讲是透明的.Github提供这种功能,可以自己指定哪些文件 ...
- Android GreenDao 在组件化项目中的一个问题 - 2018年7月5日21:15:14
组件化项目使用GreenDao时注意的事项: 1.要在组件化中的基础库(domain层)创建实体类: 2.如果sycn之后不能生产Dao文件,使用 Android Studio 的Gradle插件重新 ...
- Unity3D AssetBundle相关
Unity3D AssetBundle相关 首先,先看一下原理吧 Unity3D研究院之Assetbundle的原理(六十一) 其次,接着往下看:Unity3D研究院之Assetbundle的实战( ...
- 修改tomcat的编码方式,可以解决某些get请求乱码问题
在tomcat/conf/server.xml配置文件添加如下,修改tomcat的编码方式 <Connector URIEncoding="utf-8" connection ...
- Windows 10同步时间的方法
今天在安装了Windows 10 1809(October 2018 update)之后发现时间不能同步,以前并没有出现这种情况. 1) 打开控制面板,找到时钟域地区 2) 选择日期和时间 3) 选择 ...
- 页面引入js问题
今日问题:左侧菜单栏多余的菜单不可以滚动,自己找了很长时间,前端同事帮忙找了很长事件,最后帮我找到问题所在. 这里红色部分标识有多余部分,可以滑动是对的.但是滑动了. 问题:jquery引入的地方错了 ...
- ES6 Generator 异步编程解决方案&&&promise
Generator: 是比promise更高级的解决方案 next yield function 后加* 状态机 generator语法糖 长轮询 接口常查询 ================= ...
- stark组件开发之排序
class StartHandler(object): .......... ordered_list = [] # 排序规则由 用户指定. def get_ordered_list(self): r ...
- vue js校验金钱、数字
// 校验保留两位小数金额 export function isMoney(money) { var reg = /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1} ...