iOS开发基础38-多线程之多图片下载及缓存处理
在 iOS 开发中,处理图片的下载和缓存是一个常见需求。本文将详细介绍如何使用 NSOperationQueue
实现多图片下载,及其高级用法。同时,我们也会对比 SDWebImage
库,并分析其主要功能及底层逻辑。通过这种方式,帮助我们更高效地进行图片下载和缓存处理。
一、快速生成沙盒目录的路径
在 iOS 中,文件系统对应用提供了多个沙盒目录,每个目录有其独特的功能和用途。理解这些目录的功能和使用方法,可以帮助我们更好地管理文件和数据。
沙盒目录的各个文件夹功能
-
Documents
- 用途:保存由应用程序本身产生的文件或数据,例如:游戏进度、绘图数据等。
- iCloud 备份:目录中的文件会被自动保存在 iCloud 上。
- 注意:不要保存从网络上下载的文件,否则会导致应用无法上架。
-
Caches
- 用途:保存临时文件,"后续需要使用",例如:缓存图片,离线数据(地图数据)。
- 清理:系统不会自动清理
Caches
目录中的文件,但开发者需要提供清理解决方案。
-
tmp
- 用途:保存临时文件,"后续不需要使用"。
- 清理:
tmp
目录中的文件系统会自动清理,重新启动设备时,该目录会被清空。当系统磁盘空间不足时,系统也会自动清理该目录。
-
Preferences
- 用途:保存用户偏好设置,使用
NSUserDefaults
直接读写。 - 写入磁盘:如果需要数据及时写入磁盘,还需调用同步方法。
- 用途:保存用户偏好设置,使用
快速获取沙盒目录路径的工具类
为方便开发者快速获取沙盒目录路径,我们可以给 NSString
编写一个分类,提供获取 Documents
、Caches
和 tmp
目录路径的方法。
NSString+Path.h
#import <Foundation/Foundation.h>
@interface NSString (Path)
// 用于生成文件在 `Caches` 目录中的路径
- (instancetype)cacheDir;
// 用于生成文件在 `Documents` 目录中的路径
- (instancetype)docDir;
// 用于生成文件在 `tmp` 目录中的路径
- (instancetype)tmpDir;
@end
NSString+Path.m
#import "NSString+Path.h"
@implementation NSString (Path)
- (instancetype)cacheDir {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
return [path stringByAppendingPathComponent:[self lastPathComponent]];
}
- (instancetype)docDir {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
return [path stringByAppendingPathComponent:[self lastPathComponent]];
}
- (instancetype)tmpDir {
NSString *path = NSTemporaryDirectory();
return [path stringByAppendingPathComponent:[self lastPathComponent]];
}
@end
二、多图片下载
使用 NSOperationQueue
实现多图片下载
以下代码示例展示了如何利用 NSOperationQueue
进行多图片下载,并对下载的图片进行缓存(内存缓存和磁盘缓存)。
ViewController.m
#import "ViewController.h"
#import "XMGApp.h"
#import "NSString+Path.h"
@interface ViewController ()
@property (nonatomic, strong) NSArray *apps;
@property (nonatomic, strong) NSMutableDictionary *imageCaches;
@property (nonatomic, strong) NSMutableDictionary *operations;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.rowHeight = 150;
}
#pragma mark - UITableViewDatasource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.apps.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"app"];
XMGApp *app = self.apps[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = [NSString stringWithFormat:@"下载:%@", app.download];
cell.imageView.image = [UIImage imageNamed:@"placeholder"]; // 占位图
UIImage *image = self.imageCaches[app.icon];
if (image == nil) {
NSString *filePath = [app.icon cacheDir];
NSData *data = [NSData dataWithContentsOfFile:filePath];
if (data == nil) {
// 从网络下载图片
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op = self.operations[app.icon];
if (op == nil) {
op = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:app.icon];
data = [NSData dataWithContentsOfURL:url];
if (data == nil) {
[self.operations removeObjectForKey:app.icon];
return;
}
UIImage *image = [UIImage imageWithData:data];
self.imageCaches[app.icon] = image;
[data writeToFile:filePath atomically:YES];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.operations removeObjectForKey:app.icon];
}];
}];
self.operations[app.icon] = op;
[queue addOperation:op];
}
} else {
// 使用磁盘缓存中的图片
UIImage *image = [UIImage imageWithData:data];
self.imageCaches[app.icon] = image;
cell.imageView.image = image;
}
} else {
// 使用内存缓存中的图片
cell.imageView.image = image;
}
return cell;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
self.imageCaches = nil;
self.operations = nil;
self.apps = nil;
}
#pragma mark - Lazy Loaders
- (NSArray *)apps {
if (!_apps) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil];
NSArray *arr = [NSArray arrayWithContentsOfFile:path];
NSMutableArray *models = [NSMutableArray arrayWithCapacity:arr.count];
for (NSDictionary *dict in arr) {
XMGApp *app = [XMGApp appWithDict:dict];
[models addObject:app];
}
_apps = [models copy];
}
return _apps;
}
- (NSMutableDictionary *)imageCaches {
if (!_imageCaches) {
_imageCaches = [NSMutableDictionary dictionary];
}
return _imageCaches;
}
- (NSMutableDictionary *)operations {
if (!_operations) {
_operations = [NSMutableDictionary dictionary];
}
return _operations;
}
@end
XMGApp
模型类
#import <Foundation/Foundation.h>
@interface XMGApp : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *icon;
@property (nonatomic, copy) NSString *download;
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)appWithDict:(NSDictionary *)dict;
@end
@implementation XMGApp
- (instancetype)initWithDict:(NSDictionary *)dict {
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
+ (instancetype)appWithDict:(NSDictionary *)dict {
return [[self alloc] initWithDict:dict];
}
@end
三、SDWebImage 简介
SDWebImage
是一个强大的第三方库,用于处理图片下载、缓存及加载。以下是 SDWebImage
的主要功能及其实现原理:
1. 缓存机制
-
内存缓存:利用
NSCache
缓存图片,提高图片加载速度。- 当接收到内存警告时,自动清空内存缓存。
-
磁盘缓存:将图片保存在沙盒目录的
Caches
文件夹中的default
子目录中。- 磁盘缓存时间:默认一周。超过缓存时间的数据会自动删除。
- 磁盘清理:可以手动清除过期或所有缓存。
2. GIF 支持
SDWebImage
支持直接播放 GIF 图片。其加载过程如下:
- 下载 GIF 图片。
- 解析 GIF 图片,取出所有帧及其显示时间。
- 生成一个可动画的 UIImage 对象。
3. 图片类型识别
通过图片的前 8 个字节的十六进制数据进行图片类型识别,例如:
- PNG
- JPG
- GIF
4. 使用 SDWebImage
简化图片下载流程
通过 SDWebImage
,可以方便地实现图片下载、缓存和加载。
ViewController.m
改进
#import "ViewController.h"
#import "XMGApp.h"
#import "UIImageView+WebCache.h"
@interface ViewController ()
@property (nonatomic, strong) NSArray *apps;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.rowHeight = 150;
}
#pragma mark - UITableViewDatasource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.apps.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"app"];
XMGApp *app = self.apps[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = [NSString stringWithFormat:@"下载:%@", app.download];
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placeholder"]];
return cell;
}
#pragma mark - Lazy Loaders
- (NSArray *)apps {
if (!_apps) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil];
NSArray *arr = [NSArray arrayWithContentsOfFile:path];
NSMutableArray *models = [NSMutableArray arrayWithCapacity:arr.count];
for (NSDictionary *dict in arr) {
XMGApp *app = [XMGApp appWithDict:dict];
[models addObject:app];
}
_apps = [models copy];
}
return _apps;
}
@end
下载单张图片示例
NSURL *url = [NSURL URLWithString:@"http://example.com/image.jpg"];
[[SDWebImageManager sharedManager] loadImageWithURL:url options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL *targetURL) {
NSLog(@"正在下载:%zd / %zd", receivedSize, expectedSize);
} completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
// 下载成功
NSLog(@"下载成功:%@", image);
}
}];
结论
在 iOS 开发中,通过 NSOperationQueue
实现多图片下载,可以了解其基本原理和操作步骤。而使用 SDWebImage
库,可以大大简化图片下载和缓存管理,提升开发效率。
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。