- SDWebImage托管在github上。https://github.com/rs/SDWebImage
- 相信大家对SDWebImage这个库并不陌生,这个库提供一个UIImageView类别,以支持加载来自网络的远程图片。具有
缓存管理
、异步下载
、同一个URL下载次数控制
和优化
等特征。 - SDWebImage(新版本在各方法前加上了sd_前缀,以区分UIImageView+AFNetworking中的方法)
SDWebImage库的作用
- 通过对UIImageView的类别扩展来实现异步加载替换图片的工作。
- 主要用到的对象:
1、UIImageView (WebCache)类别,入口封装,实现读取图片完成后的回调
2、SDWebImageManager,对图片进行管理的中转站,记录那些图片正在读取。
向下层读取Cache(调用SDImageCache),或者向网络读取对象(调用SDWebImageDownloader) 。
实现SDImageCache和SDWebImageDownloader的回调。3、SDImageCache,根据URL的MD5摘要对图片进行存储和读取(实现存在内存中或者存在硬盘上两种实现)
实现图片和内存清理工作。4、SDWebImageDownloader,根据URL向网络读取数据(实现部分读取和全部读取后再通知回调两种方式)
其他类:
SDWebImageDecoder,异步对图像进行了一次解压⋯⋯
不多说,接下来,直接上代码:
SDWebImage的基本使用
- 需要下图片且需要获取下载进度
#import "UIImageView+WebCache.h"
// 内部会自动做 内存缓存 & 磁盘缓存
- (void)downloadImage1 {
/**
@param NSURL 下载图片的url地址
@param UIImage 占位符号
@param SDWebImageOptions 选项:枚举
SDWebImageRetryFailed: 默认情况下,如果一个URL在下载的时候失败了,那么这个URL会被加入黑名单,不会尝试再次下载,如果使用该参数,则该URL不会被添加到黑名单中,意味着会对下载失败URL尝试从新下载/此标记取消黑名单
SDWebImageLowPriority: //低优先级/默认情况下,在UI交互时也会启动图像下载,此标记则为取消这一特征/会退出到滚动试图停止滚动之后再继续下载图片/备注:NSURLConnection的网络下载事件监听的运行循环模式是 NSDefaultRunLoopMode
SDWebImageCacheMemoryOnly: 使用该参数,将禁止磁盘缓存,只做内存缓存
SDWebImageProgressiveDownload: //渐进式下载/此标记允许渐进式下载,就像浏览器中那样,下载过程中,图像会逐步显示出来/默认情况下,图像会在下载完成后一次性显示
SDWebImageRefreshCached: //刷新缓存/遵守HTTP相应的缓存控制,如果需要,从远程刷新图像/磁盘缓存将有NSURLCache处理,而不是SDWebImage,这会对性能有轻微的影响/此选项用于处理URL指向图片发生改变的情况/如果缓存的图像被刷新,会调用一次completion block,并传递最终的图像
SDWebImageContinueInBackground: //后台下载/如果系统版本是iOS 4+,那么当App进入后台后仍然会继续下载图像/这是想系统请求额外的后台时间保证下载请求完成的/如果后台任务过时,请求将会被取消
SDWebImageHandleCookies: 通过设置,处理保存在NSHTTPCookieStore中的cookie
SDWebImageAllowInvalidSSLCertificates: 允许不信任的SSL证书/可以出于测试目的的使用,在正式产品中慎用
SDWebImageHighPriority: // 高优先级(优先下载)/默认情况下,图像会按照添加到队列中的顺序被加载,此标记会将它们移动到队列前端被立即加载/而不是等待当前队列被加载,因为等待队列加载会需要一段时间
SDWebImageDelayPlaceholder: //延迟占位图片/默认情况下,在加载图像时,占位图片已经会被加载/此标记会延迟加载占位图像,直到图像已经完成加载
SDWebImageTransformAnimatedImage: //转换动画图像/通常不会在科动画的图像上调用transformDownloadImage代理方法,因为大多数转换代码会破坏动画文件/使用此标记尝试转换
SDWebImageAvoidAutoSetImage: //手动设置图像/下载完成后手动设置图片,默认是下载完成后自动放到ImageView上
@param progress^ 进度回调
receivedSize: 已经下载的数据大小
expectedSize: 需要下载图片的总大小
@param completed^ 图片下载完成会来到此block块
image: 要下载的图片
error: 错误信息
*cacheType: 缓存类型
imageURL: 图片URL
*/
[self.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.33lc.com/soft/UploadPic/2012-8/20128179452663218.jpg"] placeholderImage:[UIImage imageNamed:@""] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
NSLog(@"---%f-----------", 1.0 * receivedSize / expectedSize);
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
// 先做内存缓存,在做磁盘缓存
switch (cacheType) {
case SDImageCacheTypeNone:
NSLog(@"直接从网络下载");
break;
case SDImageCacheTypeDisk:
NSLog(@"从磁盘缓存");
break;
case SDImageCacheTypeMemory:
NSLog(@"从内存缓存");
break;
default:
break;
}
}];
NSLog(@"%@", [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]); //答应当前存储路径
}
- 只需要简单获得一张图片,不进行设置
#import "SDWebImageManager.h"
// 内部会自动做 内部缓存 & 磁盘缓存
- (void)downloadImage2 {
// 最核心的方法
[[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString:@"http://www.33lc.com/soft/UploadPic/2012-8/20128179452663218.jpg"] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
NSLog(@"---%f------------", 1.0 * receivedSize / expectedSize);
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
self.imageView.image = image;
}];
}
- 不需要做任何缓存
#import "SDWebImageDownloader.h"
// 没有做任何缓存处理
// 最底层的方法
- (void)downloadImage3 {
// data:图片的二进制数据
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:@"http://www.33lc.com/soft/UploadPic/2012-8/20128179452663218.jpg"] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
// 此方法并没有做线程间通信操作,需手动添加线程间通信
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
}
- 播放gif图片
#import "UIImage+GIF.h"
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
- (void)playGif {
// gif图片存在于资源库
UIImage *image = [UIImage sd_animatedGIFNamed:@"dog"];
self.imageView.image = image;
}
SDWebImage其他使用
// 当发生内存警告时会调用此方法
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
// 1.清空缓存
// clearDisk: 直接删除,然后从新重新创建
// cleanDisk: 清除过期缓存,计算当前缓存的大小,和设置的最大缓存数量比较,如果超出那么会继续删除(删除方法:按照文件创建的先后顺序)/过期缓存:7天(60*60*24*7)
[[SDWebImageManager sharedManager].imageCache cleanDisk];
// 2.取消当前所有操作
[[SDWebImageManager sharedManager] cancelAll];
// 3.最大并发数量 == 6 (SDWebImageDownLoader: maxConcurrentOperationCount <在init方法中>);
// 4.缓存文件的保存名称如何处理?拿到图片地址,然后对该路径进行md5加密,
// "补充":如何在命令行进行md5加密:echo -n "需要加密的内容" |md5
// 5.该框架内部对内存警告的处理方式?内部通过监听通知的方法清理缓存 (SDWebImageCache - )
// 6.该框架进行缓存处理的方式:可变字典(以前) ---> NSCache(现在)
// 7.如何判断图片的类型: 在判断图片类型的时候,只匹配第一个字节(NSData+ImageContentType: sd_contentTypeForImageData)
// 8.队列中任务的处理方式: FIFO: 先进先出(是一个枚举值,可以手动设置: SDWebImageDownLoader - _executionOrder = LIFO:后进先出)
// 9.如何下载图片的?发送网络请求下载图片,NSURLConnection (SDWebImageDownloaderOperation)
// 10.请求超时的时间: 15秒钟的时间
}
SDWebImage解惑
- SDImageCache是怎么做数据管理的?
- SDImageCache分两个部分,一个是内存层面的,一个是硬盘层面的
- 内存层面的相当是个缓存器,以Key-Value的形式存储图片。当内存不够的时候会清除所有缓存图片。用搜索文件系统的方式做管理,文件替换方式是以时间为单位,剔除时间大于一周的图片文件。
- 当SDWebImageManager向SDImageCache要资源时,先搜索内存层面的数据,如果有直接返回,没有的话去访问磁盘,将图片从磁盘读取出来,然后做Decoder,将图片对象放到内存层面做备份,再返回调用层.
- 为啥必须做Decoder
参考:http://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/
由于UIImage的imageWithData函数是每次画图的时候才将Data解压成ARGB的图像,所以在每次画图的时候,会有一个解压操作,这样效率很低,但是只有瞬时的内存需求。
为了提高效率通过SDWebImageDecoder将包装在Data下的资源解压,然后画在另外一张图片上,这样这张新图片就不再需要重复解压了。
这种做法是典型的空间换时间的做法。
- SDImageCache是怎么做数据管理的?
补充知识- 位移枚举简单介绍
- 第一种枚举写法
typedef enum {
VtcDemoTypeTop,
VtcDemoTypeBottom,
}VtcDemoType;
- 第二种枚举写法,定义类型
typedef NS_ENUM(NSInteger, VtcType) {
VtcTypeTop,
VtcTypeBottom,
};
- 第三种枚举写法,按位枚举
// 一个参数可以传递多个值
// 如果是位移枚举,观察第一个枚举值,如果该枚举值 != 0 那么可以默认传0做参数,如果传0做参数,那么效率更高
typedef NS_OPTIONS(NSInteger, VtcActionType) {
VtcActionTypeTop = 1<<0, //1*2(0) = 1
VtcActionTypeBottom = 1<<1, //1*2(1) = 2
VtcActionTypeleft = 1<<2, //1*2(2) = 4
VtcActionTypeRight = 1<<3, //1*2(3) = 8
};
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self demo:VtcActionTypeTop | VtcActionTypeRight | VtcActionTypeBottom | VtcActionTypeleft];
}
//按位与 & 1&1==1 1&0==0 0&0==0 只要有0则为0
//按位或 | 1|1==1 1|0==0 0|0==0 只要有1则为1
- (void)demo:(VtcType)type {
if (type & VtcActionTypeTop) {
NSLog(@"向上------%zd", type & VtcActionTypeTop);
}
if (type & VtcActionTypeleft) {
NSLog(@"向左------%zd", type & VtcActionTypeleft);
}
if (type & VtcActionTypeBottom) {
NSLog(@"向下------%zd", type & VtcActionTypeBottom);
}
if (type & VtcActionTypeRight) {
NSLog(@"向右------%zd", type & VtcActionTypeRight);
}
}
// so,options:可以传0;即效率最高,也可以使用按位或()传多个枚举,
[self.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.33lc.com/soft/UploadPic/2012-8/20128179452663218.jpg"] placeholderImage:[UIImage imageNamed:@""] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
NSLog(@"---%f-----------", 1.0 * receivedSize / expectedSize);
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
// 先做内存缓存,在做磁盘缓存
switch (cacheType) {
case SDImageCacheTypeNone:
NSLog(@"直接从网络下载");
break;
case SDImageCacheTypeDisk:
NSLog(@"从磁盘缓存");
break;
case SDImageCacheTypeMemory:
NSLog(@"从内存缓存");
break;
default:
break;
}
}];