iOS学习笔记-129.SDWebImage5——框架内部部分细节
来源:互联网 发布:linux 查看 磁盘 编辑:程序博客网 时间:2024/05/16 15:41
- SDWebImage5框架内部部分细节
- 一清空缓存
- 二取消当前所有的操作
- 三 最大并发数量
- 四缓存文件的保存名称如何处理
- 五该框架内部对内存警告的处理方式
- 六该框架进行缓存处理的方式
- 七如何判断图片的类型
- 八队列中任务的处理方式
- 九如何下载图片的
- 十请求超时的时间
SDWebImage5——框架内部部分细节
一、清空缓存
//1.清空缓存//clear:直接删除缓存目录下面的文件,然后重新创建空的缓存文件//clean:清除过期缓存,计算当前缓存的大小,和设置的最大缓存数量比较,如果超出那么会继续删除(按照文件了创建的先后顺序)//过期时间:7天[[SDWebImageManager sharedManager].imageCache cleanDisk];[[SDWebImageManager sharedManager].imageCache clearMemory];[[SDWebImageManager sharedManager].imageCache clearDisk];
二.取消当前所有的操作
[[SDWebImageManager sharedManager] cancelAll];
三. 最大并发数量
最大并发数量 6
设置的地方是 SDWebImageDownloader 的
- (id)init
方法中。
具体如下。
当我们在 UIImageView+WebCache 的下面这个方法中
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {}
使用到
SDWebImageManager.sharedManager
的时候,会调用 SDWebImageManager 的init
方法,如下
- (id)init { if ((self = [super init])) { _imageCache = [self createCache]; //初始化imageCache(单例) _imageDownloader = [SDWebImageDownloader sharedDownloader]; //初始化imageDownloader(单例) _failedURLs = [NSMutableSet new]; //初始化下载失败的URL(黑名单·空的集合) _runningOperations = [NSMutableArray new]; //初始化当前正在处理的任务(图片下载操作·空的可变数组) } return self;}
这里面会初始化 SDWebImageDownloader 的属性 imageDownloader ,这个时候回调用到 SDWebImageDownloader 的 init方法,如下
//异步下载器初始化方法- (id)init { if ((self = [super init])) { _operationClass = [SDWebImageDownloaderOperation class]; //获得类型 _shouldDecompressImages = YES; //是否解码,默认为YES(以空间换取时间) _executionOrder = SDWebImageDownloaderFIFOExecutionOrder; //下载任务的执行方式:默认为先进先出 _downloadQueue = [NSOperationQueue new]; //创建下载队列:非主队列(在该队列中的任务在子线程中异步执行) _downloadQueue.maxConcurrentOperationCount = 6; //设置下载队列的最大并发数:默认为6 _URLCallbacks = [NSMutableDictionary new]; //初始化URLCallbacks字典#ifdef SD_WEBP _HTTPHeaders = [@{@"Accept": @"image/webp,image/*;q=0.8"} mutableCopy]; //处理请求头#else _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy];#endif //创建栅栏函数添加的队列:自己创建的并发队列 _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); _downloadTimeout = 15.0; //设置下载超时为15秒 } return self;}
我们可以看到 最大并发数量 = 6
四.缓存文件的保存名称如何处理?
拿到图片的URL路径,对该路径进行MD5加密
方法是 SDImageCache中的
- (NSString *)cachedFileNameForKey:(NSString *)key
下面我们来看一下。
由前面的文章我们知道,我们的最终会调用到 SDWebImageManager 的 downloadImageWithURL
的方法。在这个方法的成功的回调里面,会进行磁盘存储。
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionWithFinishedBlock)completedBlock { ............ //是否要进行磁盘缓存? 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];#warning 2 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); } }); }); } ............ }
上面会的代码中会进行内存和磁盘缓存,具体的调用是
[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
我们进入到这个方法中看一下,好的,那么我们接下来就到了 SDImageCache 的如下代码中
//使用指定的键将图像保存到内存和可选的磁盘缓存- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk { //如果图片或对应的key为空,那么就直接返回 if (!image || !key) { return; } // if memory cache is enabled //如果内存缓存可用 if (self.shouldCacheImagesInMemory) { //计算该图片的『成本』 NSUInteger cost = SDCacheCostForImage(image); //把该图片保存到内存缓存中 [self.memCache setObject:image forKey:key cost:cost]; } //判断是否需要沙盒缓存 if (toDisk) { //异步函数+串行队列:开子线程异步处理block中的任务 dispatch_async(self.ioQueue, ^{ //拿到服务器返回的图片二进制数据 NSData *data = imageData; //如果图片存在且(直接使用imageData||imageData为空) if (image && (recalculate || !data)) {#if TARGET_OS_IPHONE // We need to determine if the image is a PNG or a JPEG // PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html) // The first eight bytes of a PNG file always contain the following (decimal) values: // 137 80 78 71 13 10 26 10 // If the imageData is nil (i.e. if trying to save a UIImage directly or the image was transformed on download) // and the image has an alpha channel, we will consider it PNG to avoid losing the transparency //获得该图片的alpha信息 int alphaInfo = CGImageGetAlphaInfo(image.CGImage); BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone || alphaInfo == kCGImageAlphaNoneSkipFirst || alphaInfo == kCGImageAlphaNoneSkipLast); //判断该图片是否是PNG图片 BOOL imageIsPng = hasAlpha; // But if we have an image data, we will look at the preffix if ([imageData length] >= [kPNGSignatureData length]) { imageIsPng = ImageDataHasPNGPreffix(imageData); } //如果判定是PNG图片,那么把图片转变为NSData压缩 if (imageIsPng) { data = UIImagePNGRepresentation(image); } else { //否则采用JPEG的方式 data = UIImageJPEGRepresentation(image, (CGFloat)1.0); }#else data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];#endif } if (data) { //确定_diskCachePath路径是否有效,如果无效则创建 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类型 NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey]; //使用文件管理者在缓存路径创建文件,并设置数据 [_fileManager createFileAtPath:cachePathForKey contents:data attributes:nil]; // disable iCloud backup //如果禁用了iCloud备份 if (self.shouldDisableiCloud) { //标记沙盒中不备份文件(标记该文件不备份) [fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil]; } } }); }}
上面的代码中,我们关注下面的代码
// 根据key获得缓存路径NSString *cachePathForKey = [self defaultCachePathForKey:key];
接下来我们看到 defaultCachePathForKey 方法
//获得指定 key 的默认缓存路径- (NSString *)defaultCachePathForKey:(NSString *)key { return [self cachePathForKey:key inPath:self.diskCachePath];}
然后我们再到 - (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path
//获得指定 key 对应的缓存路径- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path { //获得缓存文件的名称 NSString *filename = [self cachedFileNameForKey:key]; //返回拼接后的全路径 return [path stringByAppendingPathComponent:filename];}
发现它调用了 - (NSString *)cachedFileNameForKey:(NSString *)key
方法,如下
//对key(通常为URL)进行MD5加密,加密后的密文作为图片的名称- (NSString *)cachedFileNameForKey:(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[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]]; return filename;}
现在我们看到了,它是通过 MD5 来处理URL地址的,然后用作文件名。
五、该框架内部对内存警告的处理方式?
内部通过监听通知的方式清理缓存
如下,
既然是内存警告的处理,那么我们想到就是缓存的问题,我们来到 SDImageCache 中
如下
//初始化- (id)init{ self = [super init]; if (self) { //监听到UIApplicationDidReceiveMemoryWarningNotification(应用程序发生内存警告)通知后,调用removeAllObjects方法 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; } return self;}
发生内存警告的时候,清除内存中的所有缓存对象。
除此之外,还有下面的方法中也有
//使用指定的命名空间实例化一个新的缓存存储和目录- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory { ............#if TARGET_OS_IPHONE // 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;}
六、该框架进行缓存处理的方式
既然是看缓存的处理,那么我们还是直接看 SDImageCache 类,发现它是继承自 NSCache的,
那么也就是说它是用 NSCache 来处理缓存的。
七.如何判断图片的类型
在判断图片类型的时候,只匹配第一个字节。这个的处理在 NSData+ImageContentType中
//// Created by Fabrice Aneche on 06/01/14.// Copyright (c) 2014 Dailymotion. All rights reserved.//#import "NSData+ImageContentType.h"@implementation NSData (ImageContentType)+ (NSString *)sd_contentTypeForImageData:(NSData *)data { uint8_t c; //获得传入的图片二进制数据的第一个字节 [data getBytes:&c length:1]; //在判断图片类型的时候,只匹配第一个字节 switch (c) { case 0xFF: return @"image/jpeg"; case 0x89: return @"image/png"; case 0x47: return @"image/gif"; case 0x49: case 0x4D: return @"image/tiff"; case 0x52: //WEBP :是一种同时提供了有损压缩与无损压缩的图片文件格式 // R as RIFF for WEBP if ([data length] < 12) { return nil; } //获取前12个字节 NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding]; //如果以『RIFF』开头,且以『WEBP』结束,那么就认为该图片是Webp类型的 if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) { return @"image/webp"; } //否则返回nil return nil; } return nil;}@end@implementation NSData (ImageContentTypeDeprecated)+ (NSString *)contentTypeForImageData:(NSData *)data { return [self sd_contentTypeForImageData:data];}@end
八、队列中任务的处理方式
任务的处理方式 FIFO。
这个体现在,SDWebImageDownloader 的 init
方法中
//异步下载器初始化方法- (id)init { ···· _executionOrder = SDWebImageDownloaderFIFOExecutionOrder; //下载任务的执行方式:默认为先进先出 ···· return self;}
我们可以看一下,SDWebImageDownloaderExecutionOrder 枚举
//下载操作的执行方式typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) { /* * 默认值,所有下载操作将按照队列的先进先出方式执行 */ SDWebImageDownloaderFIFOExecutionOrder, /* * 所有下载操作将按照堆栈的后进先出方式执行 */ SDWebImageDownloaderLIFOExecutionOrder};
九.如何下载图片的?
发送网络请求下载图片,使用NSURLConnection
具体体现,我们来到 SDWebImageDownloaderOperation 类中,看到它的 “start“`方法
//核心方法:在该方法中处理图片下载操作- (void)start { .... //创建NSURLConnection对象,并设置代理(没有马上发送请求) self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; .... [self.connection start]; //发送网络请求 ....}
十.请求超时的时间
请求超时的时间,是 15s。
其实这个问题我们在 iOS学习笔记-128.SDWebImage4——框架内部调用简单分析 中就说过啦。
下面我们再来看一下,来到 SDWebImageDownloader 的下面这个方法中,我们会发现,超时时间就是 15s.
//核心方法:下载图片的操作- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock { .......... //处理下载超时,如果没有设置过则初始化为15秒 NSTimeInterval timeoutInterval = wself.downloadTimeout; if (timeoutInterval == 0.0) { timeoutInterval = 15.0; } ..........}
- iOS学习笔记-129.SDWebImage5——框架内部部分细节
- iOS学习笔记-128.SDWebImage4——框架内部调用简单分析
- 4.笔记JAVA框架学习——内部Bean
- ios学习笔记:ios推送细节
- iOS学习笔记13—iOS框架总结
- JAVA NIO学习笔记--缓冲区的内部细节
- windows下scrapy框架学习笔记—'scrapy' 不是内部或外部命令
- windows下scrapy框架学习笔记—'scrapy' 不是内部或外部命令
- .Net学习笔记——细节问题
- laravel 框架部分细节记录
- IOS学习笔记6—Objective C—Foundation框架
- 黑马程序员——IOS学习笔记(Foundation框架(一))
- 黑马程序员——IOS学习笔记(Foundation框架(二))
- iOS——Core Graphic框架学习笔记
- iOS学习笔记-086.彩票01——框架搭建
- Foundation of Machine Learning 笔记第一部分——PAC学习框架
- [ExtJS5学习笔记]第三节 sencha cmd学习笔记 生成应用程序构建的内部细节
- AutoLayout技术细节笔记--部分
- QT学习之QString的arg方法
- 仿站小工具3.0 趴网站源码
- 安卓免Root脚本精灵2.0.5
- 面试中常考的几个集合类(java)
- HTML5---H5---CSS常用的选择器
- iOS学习笔记-129.SDWebImage5——框架内部部分细节
- 京东2018秋招前端笔试编程题
- selenium tips
- 宝塔面板|Linux网站控制面板
- 安卓大圣归来短信轰炸机
- 再谈袁萌为何鸣冤叫屈?
- 破解js屏蔽chromeF12后页面自动跳转的解决方法
- Android四大核心组件之Activity
- Requirejs高级应用(七):模块导出的三种方式及优先级