0709-NSOperationQueue多图片下载
1、当一个控制器存在多个scrollView时最好把automaticallyAdjustsScrollViewInsets
属性的值设为NO(01-单例模式注意)
2、NSOperation(03-nsoperationqueue基本使用)
1. 任务
- NSInvocationOperation(很少用)
- (void)invocationOperation
{
//这样不加入队列只分配任务就执行,默认就是在主线程
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//加入队列就不用star会自动开启
[op start];
}
- NSBlockOperation(很少用)
- (void)blockOperation
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主线程
NSLog(@"下载1------%@", [NSThread currentThread]);
}];
// 添加额外的任务(在子线程执行)
[op addExecutionBlock:^{
NSLog(@"下载2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载4------%@", [NSThread currentThread]);
}];
//加入队列就不用star会自动开启
[op start];
}
- 自定义NSOperation
- 新建一个继承NSOperation的子类
- 重写main方法
//需要执行的任务
- (void)main
{
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
}
- 最后allocinit再加入队列就行了
XMGOperation *op5 = [[XMGOperation alloc] init];
// 添加任务到队列中
[queue addOperation:op5]; // [op5 start]
2. 队列(NSOperationQueue)
和GCD的联系
- GCD的队列类型
- 并发队列
- 自己创建的
- 全局
- 串行队列
- 主队列
- 自己创建的
- NSOperationQueue的队列类型
- 主队列
- [NSOperationQueue mainQueue]
- 凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行
- 非主队列(其他队列)
- [[NSOperationQueue alloc] init]
- 同时包含了:串行、并发功能
- 添加到这种队列中的任务(NSOperation),就会自动放到子线程中执行
重要说明
- 关键属性maxConcurrentOperationCount(最大并发操作数)(04-nsoperationqueue的最大并发数)
- 概念:NSOperationQueue队列串行还是并发全由这个属性决定,当其设置为1时就是串行,大于1时就是并发
- 注意:这个属性的概念是指同一时间执行任务的线程个数,至于开几条线程来准备执行任务由系统决定!!这里需要理解
- 重要属性suspended(05-nsoperationqueue的挂起和取消)
- 概念:为NO恢复队列,继续执行;为YES暂停(挂起)队列,暂停执行
- 注意:这里说的暂停(也叫挂起),并不是中断一个任务的执行(一个任务一旦开启只有他执行完毕才会停下来,所以这是不可控的),而是指当前任务执行完后停下来,下个任务不执行了!!这里同样需要理解下
- 有用属性isCancelled(特别是结合自定义队列非常有用)(05-nsoperationqueue的挂起和取消)
鉴于第二点里面注意所说的,一个任务在开启后只会在执行完毕才会结束,那么🍎官方的建议是对一些耗时操作的任务,在多处地方进行isCancelled属性判断,就能够达到当使用
[self.queue cancelAllOperations]
这个方法时产生“ 中断一个任务的执行”的效果(适用场景:销毁一个控制器时中断他的网络请求,避免产生不必要的崩溃)
- 依赖addDependency和监听完成completionBlock
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2----%@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3----%@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"download4----%@", [NSThread currentThread]);
}
}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download5----%@", [NSThread currentThread]);
}];
op5.completionBlock = ^{
NSLog(@"op5执行完毕---%@", [NSThread currentThread]);
};
// 设置依赖
[op3 addDependency:op1];
[op3 addDependency:op2];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
}
3、小栗子:多图片下载(08-10多图片下载01-显示数据)
知识要点
关于沙盒的三个文件夹
- Documents
下载的文件不能保存在这里,当手机连接iTunes时,会将这个文件夹备份到苹果的服务器。而且审核的时候发现下载东西如果保存到这里会被拒绝。 - Library
- Caches
希望下载的图片永远存在放这里 - Preference
- tmp
希望下载的图片临时存在放这里
手动实现图片下载的思路(01-多图片下载最终解决方案)
- 流程
- 从内存加载图片
- 若内存没有,从沙盒加载图片,并放一份到内存中
- 若沙盒也没有,从网络下载图片,下载完后写到内存和沙盒中
- 注意
在最后这一步的下载中要注意很多细节问题:下载完成的赋值问题、下载还没有完成前的重复下载问题导、下载失败的问题,其他还有缓存周期、大小的设置
- 下载完成赋值不能简单的用
cell.imageView.image = image
,这样会导致数据错乱,这是因为cell的循环利用会导致原来开启下载的第M行cell在被循环利用到第N行下载才完成,这样原本是第M行的图片就会出现在第N行,解决办法是用([tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
)代替简单的直接赋值(cell.imageView.image = image
),因为此时下载已经完成,图片已经被写入内存和沙盒中,所以刷新这一行时会直接从内存中取第M行的图片而不会再走下载流程 - 重复下载问题指的是再次刷到第M行cell的时候原先的下载还没有完成便又开启一个新的下载,解决办法:建一个下载操作对象的字典,这个字典的Key就是每个图片下载的URL,开启下载前判断字典里是否存在,不存在就下载,并写入字典,下载完成后从字典移除(下载完成后会写入沙盒和内存,以后再刷新这行cell就不会走下载流程了)
- 下载失败解决办法:把操作对象从操作字典里移除(这样才能下次再开启下载),然后返回
- 缓存周期、大小设置:定期删除缓存(SDWebImage默认是一周),最多缓存数量
- 更完善的做法是在控制器的内存警告方法里清空内存缓存和取消下载任务
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
self.images = nil;
self.operations = nil;
[self.queue cancelAllOperations];
}
网友评论