NSOperationQueue
- NSOperation的作用
- 配合使用NSOperation和NSOperationQueue也能实现多线程编程
- NSOperation和NSOperationQueue实现多线程的具体步骤
- 先将需要执行的操作封装到到一个NSOperation对象中
- 然后将NSOperation对象添加到NSOperationQueue中
- 系统会自动将 NSOperationQueue中的NSOperation取出来
NSOperation的子类
- NSOperation是个抽象类,布局类封装操作的能力,必须使用它的子类
- 使用NSOperation子类的方式有3种
- NSInvocationOperation
- NSBlockOperation
- 自定义子类继承NSOperation,实现内部相应的方法
NSInvocationOperation
- 创建NSInvocationOperation对象
-(id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
- 调用start方法开始执行操作
- -(void)start;
- 一旦执行操作,就会调用target的sel方法
- 注意
- 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
- 只有将 NSOperation放到一个NSOperationQueue中,才会异步执行操作
NSBlockOperation
- 创建NSBlockOperation对象
+(id)blockOperationWithBlock:(void(^)(void))block;
- 通过addExecutionBlock方法添加更多的操作
-(void)addExecutionBlock:(void(^)(void))block;
- 注意:只要NSBlockOperation封装的操作数>1,就会异步执行操作
自定义子类继承NSOperation
- 步骤:重写-(void)main方法,在里面实现想执行的任务
- 可以选择实现is current等函数
- 重写-(void)main方法的注意点
- 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
- 经常通过-(BOOL)isCancelled方法检测操作是否被取消,对取消作出响应
NSOperationQueue的使用
- 作用:NSOperation可以调用start方法来执行任务,但默认是同步执行的,如果NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
- NSOperationQueue的队列类型
- 主队列
- [NSOperationQueue mainQueue]
- 凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行
- 非主队列/ 其他队列(串行、并发)
- [[NSOperationQueue alloc]init]
- 同时包含了串行、并发功能
- 添加到这种队列中的任务(NSOperation),就会自动放到子线程中执行
- 主队列
- 可以直接把任务加进去
[queue addOperationWithBlock:(void(^)(void))block
- maxConcurrentOperationCount参数值,用于控制最大并发数。默认值是-1。当这个值为1时,就变成了串行。如果为0,表示没有并发数,即任务都不会执行
- suspended。暂停/挂起。该属性为YES,表示暂停/挂起;该属性为NO,任务继续执行
- 取消所有操作
[queue cancelAllOperations];
相当于调用了当前所有operation的cancel方法 - 注意:无论是暂停还是取消,针对的都是整个任务,都是在当前任务执行完毕后才会暂停或取消
- 操作依赖。
[op3 addDependency:op1];
意味着op1执行完毕,才能执行op3- NSOperation之间可以设置依赖来保证执行顺序
- 可以在不同queue的NSOperation之间创建依赖关系
- 注意:不能相互依赖。比如A依赖B,B依赖A
- 操作的监听
- 可以监听一个操作的执行完毕
-(void)(^)(void))completionBlock;
-(void)setCompletionBlock:(void)(^)void))block;
- 可以监听一个操作的执行完毕
线程间的通讯
- 示例代码
[[[NSOperationQueue alloc]init] addOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1503992696462&di=e51b985b9790aceef17d1d8e80f9c4b8&imgtype=0&src=http%3A%2F%2Fimglf0.ph.126.net%2F1EnYPI5Vzo2fCkyy2GsJKg%3D%3D%2F2829667940890114965.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
- 在子线程下载图片,下载完成后回到主线程
__block UIImage *image1 = nil;//为了防止整个大括号代码走完,image1还没用完,就加个__block,防止销毁
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//下载图片1
NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
}];
__block UIImage *image2 = nil;
//下载图片2
NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
}];
//合并图片
NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
//合并完后,可以给iamge12赋值为nil,进行销毁
//回到主线程
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
}];
}];
[combine addDependency:download1];
[combine addDependency:download2];
[queue addOperation:download1];
[queue addOperation:download2];
[queue addOperation:combine];
多图片下载
自定义NSOperation下载图片思路-有沙盒缓存
示例代码
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"app"];
XXApps *app = self.appArray[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.download;
UIImage *image = self.imageCache[app.icon];
if (image) {
cell.imageView.image = image;
}else{
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
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.imageCache[app.icon] = image;
}else{//下载图片
cell.imageView.image = [UIImage imageNamed:@"阿狸头像"];
NSOperation *op = self.operations[app.icon];
if (op == nil) {//这张图片暂时没有下载任务
op = [NSBlockOperation blockOperationWithBlock:^{
//下载图片
NSURL *url = [NSURL URLWithString:app.icon];
NSData *data = [NSData dataWithContentsOfURL:url];
//数据加载失败
if (data == nil){
//移除操作
[self.operations removeObjectForKey:app.icon];
return;
}
UIImage *image = [UIImage imageWithData:data];
//存到字典中
self.imageCache[app.icon] = image;
//回到主线程显示图片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
//将图片文件数据写入到沙盒中
[data writeToFile:file atomically:YES];
//移除操作
[self.operations removeObjectForKey:app.icon];
}];
//添加到队列中
self.operations[app.icon] = op;
//存放到字典中
[self.queue addOperation:op];
}
NSLog(@"%@",file);
// NSDictionary *dic = @{
// file:[NSDate date]
// };
}
NSLog(@"%@",file);
}
//获取文件属性
// [NSFileManager defaultManager]attributesOfItemAtPath:<#(nonnull NSString *)#> error:<#(NSError * _Nullable __autoreleasing * _Nullable)#>
return cell;
}