1. SDWebImage框架基本介绍
1.1 SDWebImage实现的功能
1.提供了一个UIImageView的category用来加载网络图片并且对网络图片的缓存进行管理
2.采用异步方式来下载网络图片
3.采用异步方式,使用memory+disk来缓存网络图片,自动管理缓存。
4.支持GIF动画
5.支持WebP格式
6.同一个URL的网络图片不会被重复下载
7.失效的URL不会被无限重试
8.耗时操作都在子线程,确保不会阻塞主线程
9.使用GCD和ARC
10.支持Arm64
1.2 SDWebImage的工作原理
![](https://img.haomeiwen.com/i4842861/9a7004de76c1dbdf.png)
1.3 SDWebImage的结构
![](https://img.haomeiwen.com/i4842861/74c90daf4a9d1cad.png)
1.4 SDWebImage的基本使用
(1)使用IImageView+WebCache category来加载UITableView中cell的图片
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
(2)使用Blocks,采用这个方案可以在网络图片加载过程中得知图片的下载进度和图片加载成功与否
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
//... completion code here ...
}];
(3)使用SDWebImageManager,SDWebImageManager为UIImageView+WebCache category的实现提供接口
SDWebImageManager *manager = [SDWebImageManager sharedManager] ;
[manager downloadImageWithURL:imageURL options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
// do something with image
}
}];
(4)获取SDWebImage的磁盘缓存大小
[SDImageCache sharedImageCache] getSize];
(5)清理内存缓存,清理内存中缓存的图片资源,释放内存资源
[[SDImageCache sharedImageCache] clearMemory];
2. SDWebImage 解析
解析主要围绕着SDWebImage的图片加载流程来分析,介绍SDWebImage这个框架加载图片过程中的一些处理方法和设计思路。
(1) 给UIImageView设置图片,然后SDWebImage调用这个最终的图片加载方法。
![](https://img.haomeiwen.com/i4842861/0dac1b2a51822157.png)
(2)开始加载之前图片先取消对应的UIImageView先前的图片下载操作。试想,如果我们给UIImageView设置了一张新的图片,那么我们还会在意该UIImageVIew先前是要加载哪一张图片么?应该是不在意的吧!那是不是应该尝试把该UIImageView先前的加载图片相关操作给取消掉呢。
[self sd_cancelCurrentImageLoad]
![](https://img.haomeiwen.com/i4842861/904f4c0d0d8cbe14.png)
该方法经过周转,最后调用了以下方法,框架将图片对应的下载操作放到UIView的一个自定义字典属性(operationDictionary)中,取消下载操作第一步也是从这个UIView的自定义字典属性(operationDictionary)中取出所有的下载操作,然后依次调用取消方法,最后将取消的操作从(operationDictionary)字典属性中移除。
![](https://img.haomeiwen.com/i4842861/711de7748d7565c2.png)
(3)移除之前没用的图片下载操作之后就创建一个新的图片下载操作,然后设置到UIView的一个自定义字典属性(operationDictionary)中。
![](https://img.haomeiwen.com/i4842861/86b2114de4720fd9.png)
(4)看看如何创建一个新的图片下载操作,框架保存了一个失效的URL列表,如果URL失效了就会被加入这个列表,保证不会重复多次请求失效的URL。
![](https://img.haomeiwen.com/i4842861/8ba4627ad097ba40.png)
根据给定的URL生成一个唯一的Key,之后利用这个key到缓存中查找对应的图片缓存。
![](https://img.haomeiwen.com/i4842861/4bb42ed6163215e9.png)
(5)读取图片缓存,根据key先从内存中读取图片缓存,若没有命中内存缓存则读取磁盘缓存,如果磁盘缓存命中,那么将磁盘缓存读到内存中成为内存缓存。如果都没有命中缓存的话,那么就在执行的doneBlock中开始下载图片。
![](https://img.haomeiwen.com/i4842861/98e9ab9348759f9a.png)
(6)图片下载操作完成后会将图片对应的数据通过completed Block进行回调。
![](https://img.haomeiwen.com/i4842861/be0bf0d0fd84846f.png)
在图片下载方法中,调用了一个方法用于添加创建和下载过程中的各类Block回调。
![](https://img.haomeiwen.com/i4842861/b9bb8b900b1e72ad.png)
添加该URL加载过程的状态回调Block
![](https://img.haomeiwen.com/i4842861/f0c4bc4b4425e076.png)
如果该URL是第一次加载的话,那么就会执行createCallback这个回调Block,然后在createCallback里面开始构建网络请求,在下载过程中执行各类进度Block回调。
![](https://img.haomeiwen.com/i4842861/08aa10122f185731.png)
(7)当图片下载完成之后会回到done的Block回调中做图片转换处理和缓存操作
![](https://img.haomeiwen.com/i4842861/d38c543fb5801a49.png)
回到UIImageView控件的设置图片方法Block回调中,给对应的UIImageView设置图片,操作流程到此完成。
![](https://img.haomeiwen.com/i4842861/61435dfdf03d4bb5.png)
3. 仿SDWebImage思路自己实现网络图片加载
@interface ViewController ()
// plist文件数据的容器
@property(nonatomic, strong) NSArray *appList;
/**管理全局下载操作的队列*/
@property(nonatomic, strong) NSOperationQueue *opQueue;
/**所有的下载操作的缓冲池*/
@property(nonatomic, strong) NSMutableDictionary *operationCache;
/**所有图像的在内存的缓存*/
@property(nonatomic, strong) NSCache *imageCache;
@end
@implementation ViewController
- (NSCache *)imageCache
{
if (_imageCache == nil) {
_imageCache = [[NSCache alloc] init];
_imageCache.countLimit = 10;
}
return _imageCache;
}
- (NSMutableDictionary *)operationCache
{
if (_operationCache == nil) {
_operationCache = [NSMutableDictionary dictionary];
}
return _operationCache;
}
- (NSOperationQueue *)opQueue
{
if (_opQueue == nil) {
_opQueue = [[NSOperationQueue alloc]init];
}
return _opQueue;
}
// 其余待码省......
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"AppCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 给cell设置数据
LLApp *app = self.appList[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.download;
// 判断当前图片缓存里面是否有图像
if ([self.imageCache objectForKey:app.icon]) { // 内存有图片
NSLog(@"没有上网下载.....");
cell.imageView.image = [self.imageCache objectForKey:app.icon];
}else{ // 内存没有图片
// 如果沙盒里面有图片,直接从沙盒加载
UIImage *image = [UIImage imageWithContentsOfFile:[self cachePathWithUrl:app.icon]];
if (image) { // 沙盒有图片
NSLog(@"从磁盘加载图像,,沙盒");
// 1. 添加图片到内存,方便下次从内存直接加载
[self.imageCache setObject:image forKey:app.icon];
// 2. 显示图片到cell
cell.imageView.image = [self.imageCache objectForKey:app.icon];
}else{ // 沙盒里面没有图片,显示占位图,网上下载
// 显示占位图
cell.imageView.image = [UIImage imageNamed:@"user_default"];
// 下载图片
[self downloadImage:indexPath];
}
}
return cell;
}
/**下载图像*/
- (void)downloadImage:(NSIndexPath *)indexPath
{
LLApp *app = self.appList[indexPath.row];
// 判断缓冲池中是否存在当前图片的操作
if (self.operationCache[app.icon]) {
NSLog(@"正在玩命下载中。。。。");
return;
}
// 没有下载操作,创建异步下载操作,来下载图片
__weak typeof(self) weakSelf = self;
NSBlockOperation *downloadOp = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"正在下载中......");
// 1. 下载图片(二进制)
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
UIImage *image = [UIImage imageWithData:data];
// 2. 将下载的数据保存到模型
if (image) {
[weakSelf.imageCache setObject:image forKey:app.icon];
// 将图片写入沙盒
[data writeToFile:[self cachePathWithUrl:app.icon] atomically:YES];
}
// 3. 将操作从操作缓冲池删除
[weakSelf.operationCache removeObjectForKey:app.icon];
// 4. 更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// 刷新当前行
[weakSelf.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
}];
// 将操作添加到下载缓冲池
[self.opQueue addOperation:downloadOp];
NSLog(@"操作的数量--->%tu", self.opQueue.operationCount);
// 将操作添加到缓冲池中(使用图片的url作为key)
[self.operationCache setObject:downloadOp forKey:app.icon];
}
/**
在真实开发中,一定要注意这个方法。
*/
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// 需要在这里做一些内存清理工作. 如果不处理,会被系统强制闪退。
// 清理图片的缓存
[self.imageCache removeAllObjects];
// 清理操作缓存
[self.operationCache removeAllObjects];
// 取消下载队列里面的任务
[self.opQueue cancelAllOperations];
}
/**
拼接一个文件在沙盒的全路径
*/
- (NSString *)cachePathWithUrl:(NSString *)urlStr
{
// 1. 获得缓存的路径
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
// 2. 把路径跟urlStr拼接起来
return [cachePath stringByAppendingPathComponent:urlStr.lastPathComponent];
}
网友评论