iOS开发基础38-多线程之多图片下载及缓存处理
1.iOS开发基础1-第一个iOS程序2.iOS开发基础2-基础控件3.iOS开发基础6-懒加载、Plist 文件操作、字典转模型、自定义 View 详解4.iOS开发基础5-UIButton5.iOS开发基础4-图像资源6.iOS开发基础3-UIImage7.iOS开发基础14-KVC的应用与底层逻辑8.iOS开发基础13-深入理解 UITableView(二)9.iOS开发基础12-深入理解UITableView(一)10.iOS开发基础11-屏幕适配、Autolayout及 Masonry 框架11.iOS开发基础10-UIButton内边距和图片拉伸模式12.iOS开发基础9-提示框(UIAlertController)13.iOS开发基础8-UIScrollView14.iOS开发基础7-自定义构造方法、layoutSubviews、Xib文件与自定义View15.iOS开发基础30-UITabBarController16.iOS开发基础29-触摸事件及手势识别17.iOS开发基础28-数据存储与沙盒机制18.iOS开发基础27-导航控制器入栈与出栈机制及微博个人详情页19.iOS开发基础26-空20.iOS开发基础25-ARC和MRC深入探析21.iOS开发基础24-UIPickerView、UITextField、KVC、UIDatePicker、控制器及导航控制器22.iOS开发基础23-iOS开发中的Info.plist、UIApplication及其Delegate、UIWindow详解23.iOS开发基础22-键盘通知在iOS开发中的应用24.iOS开发基础21-深入理解通知、代理、KVO和Block在iOS开发中的应用25.iOS开发基础20-UITableView的全局及局部刷新、左滑操作与批量删除26.iOS开发基础19-深入理解和实现不等高的 UITableViewCell27.iOS开发基础18-深入理解 Objective-C Runtime 机制28.iOS开发基础16-使用 `NSTimer` 时避免内存泄露的技巧和最佳实践29.iOS开发基础15-KVO的应用与底层逻辑30.iOS开发基础65-iPad 开发指南31.iOS开发基础64-二维码32.iOS开发基础63-AVFoundation/MediaPlayer33.iOS开发基础62-音频播放34.iOS开发基础61-通讯录35.iOS开发基础60-传感器36.iOS开发基础59-内存优化37.iOS开发基础58-支付宝集成指南38.iOS开发基础57-换肤功能与静态库开发指南39.iOS开发基础56-UIDynamic物理引擎40.iOS开发基础55-利用 UIWindow 实现快速滚动到界面顶部41.iOS开发基础54-CoreLocation42.iOS开发基础53-MapKit 框架43.iOS开发基础47-iOS键盘44.iOS开发基础46-数据安全与HTTPS保护详解45.iOS开发基础45-UIWebview46.iOS开发基础44-网络编程之NSURLSession&AFN47.iOS开发基础43-CocoaPods48.iOS开发基础42-网络编程之文件下载与处理49.iOS开发基础41-网络编程之JSON和XML50.iOS开发基础40-网络编程之NSURLConnection51.iOS开发基础39-RunLoop
52.iOS开发基础38-多线程之多图片下载及缓存处理
53.iOS开发基础37-多线程之NSOperation54.iOS开发基础36-多线程之GCD55.iOS开发基础35-多线程之NSThread56.iOS开发基础34-多线程57.iOS开发基础33-核心动画(二)58.iOS开发基础33-核心动画(一)59.iOS开发基础32-Quartz2D(二)60.iOS开发基础32-Quartz2D(一)61.iOS开发基础31-Modal 与 Push 详解62.iOS开发基础76-iOS 开发中的属性修饰符详解63.iOS开发基础75-iOS开发中的Block深度解析64.iOS开发基础74-Swift他来了65.iOS开发基础73-24种性能优化技巧66.iOS开发基础72-Xcode 7 升级后的问题与解决方案详解67.iOS开发基础71-应用中的 StatusBar 详解68.iOS开发基础70-TCP与UDP69.iOS开发基础69-应用开发中的 Controller 间通信模式70.iOS开发基础68-图片轮播71.iOS开发基础67-流水布局相册缩放72.iOS开发基础66-UISearchBar 控件指南73.iOS开发基础78-iOS 国际化在 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
库,可以大大简化图片下载和缓存管理,提升开发效率。
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!