SDWebImage框架是为UIImageView添加的一个分类,提供了远程图片下载的缓存机制。
缓存机制大概的思路:
1、提供两个字典,一个字典中用将图片的URL作为Key图片作为value来保存,用来做内存缓存。还有一个字典将图片的URL作为Key下载图片的操作作为value。
2、首先根据URL去缓存中找是否有图片。
3、缓存中没有就去沙盒中查找是否有图片(根据URL的MD5生成唯一的图片名字)。
4、当沙盒中没有暂时设置占位图片。(设置占位图片的另一个原因是设置图片的尺寸)
5、发送请求,从网络中获取图片。
6、根据URL判断当前操作有没有正在下载。
7、没有下载,创建一个操作添加到队列中来异步下载图片,并将这个操作存放到字典中
8、下载失败,移除字典中的下载操作(防止下次不能下载),并返回。
9、下载成功,将图片放入缓存中。
10、回到主线程刷新`添加操作`这一行的cell(不直接设置图片的原因是防止循环利用显示出错)。
11、将图片写入沙盒(耗时操作尽量放到后面)。
12、移除操作。
#import <Foundation/Foundation.h>
@interface XDYBApp : NSObject
/** 图标 */
@property (nonatomic, strong) NSString *icon;
/** 下载量 */
@property (nonatomic, strong) NSString *download;
/** 名字 */
@property (nonatomic, strong) NSString *name;
+ (instancetype)appWithDict:(NSDictionary *)dict;
@end
#import "XDYBApp.h"
@implementation XDYBApp
+ (instancetype)appWithDict:(NSDictionary *)dict
{
XDYBApp *app = [[self alloc] init];
[app setValuesForKeysWithDictionary:dict];
return app;
}
@end
#import "ViewController.h"
#import "XDYBApp.h"
@interface ViewController ()
/** 所有数据 */
@property (nonatomic, strong) NSArray *apps;
/** 内存缓存的图片 */
@property (nonatomic, strong) NSMutableDictionary *images;
/** 所有的操作对象 */
@property (nonatomic, strong) NSMutableDictionary *operations;
/** 队列对象 */
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
- (NSOperationQueue *)queue
{
if (!_queue) {
_queue = [[NSOperationQueue alloc] init];
_queue.maxConcurrentOperationCount = 3;
}
return _queue;
}
- (NSMutableDictionary *)operations
{
if (!_operations) {
_operations = [NSMutableDictionary dictionary];
}
return _operations;
}
- (NSMutableDictionary *)images
{
if (!_images) {
_images = [NSMutableDictionary dictionary];
}
return _images;
}
- (NSArray *)apps
{
if (!_apps) {
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]];
NSMutableArray *appArray = [NSMutableArray array];
for (NSDictionary *dict in dictArray) {
[appArray addObject:[XDYBApp appWithDict:dict]];
}
_apps = appArray;
}
return _apps;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
self.images = nil;
self.operations = nil;
[self.queue cancelAllOperations];
}
#pragma mark - 数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.apps.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"app";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
XDYBApp *app = self.apps[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.download;
// 先从内存缓存中取出图片
UIImage *image = self.images[app.icon];
if (image) { // 内存中有图片
cell.imageView.image = image;
} else { // 内存中没有图片
// 获得Library/Caches文件夹
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
// 获得文件名
NSString *filename = [app.icon lastPathComponent];
// 计算出文件的全路径
NSString *file = [cachesPath stringByAppendingPathComponent:filename];
// 加载沙盒的文件数据
NSData *data = [NSData dataWithContentsOfFile:file];
if (data) { // 直接利用沙盒中图片
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
// 存到字典中
self.images[app.icon] = image;
} else { // 下载图片
cell.imageView.image = [UIImage imageNamed:@"placeholder"];
NSOperation *operation = self.operations[app.icon];
if (operation == nil) { // 这张图片暂时没有下载任务
operation = [NSBlockOperation blockOperationWithBlock:^{
// 下载图片
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
// 数据加载失败
if (data == nil) {
// 移除操作
[self.operations removeObjectForKey:app.icon];
return;
}
UIImage *image = [UIImage imageWithData:data];
// 存到字典中
self.images[app.icon] = image;
// 回到主线程显示图片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
// 将图片文件数据写入沙盒中
[data writeToFile:file atomically:YES];
// 移除操作
[self.operations removeObjectForKey:app.icon];
}];
// 添加到队列中
[self.queue addOperation:operation];
// 存放到字典中
self.operations[app.icon] = operation;
}
}
}
return cell;
}
@end
网友评论