NSOperation
- NSOperation的作用
配合使用NSOperation和NSOperationQueue也能实现多线程编程
- NSOperation和NSOperationQueue实现多线程的具体步骤
1)先将需要执行的操作封装到一个NSOperation对象中
2)然后将NSOperation对象添加到NSOperationQueue中
3)系统会自动将NSOperationQueue中的NSOperation取出来
4)将取出的NSOperation封装的操作放到一条新线程中执行
- NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类, 使用NSOperation子类的方式有3种
1)NSInvocationOperation
2)NSBlockOperation
3)自定义子类继承NSOperation,实现内部相应的方法
NSInvocationOperation
- 创建NSInvocationOperation对象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
- 调用start方法开始执行操作
- (void)start;
一旦执行操作,就会调用target的sel方法
- 注意
1)默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
2)只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
NSBlockOperation
- 创建NSBlockOperation对象
+ (id)blockOperationWithBlock:(void (^)(void))block;
- 通过addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void (^)(void))block;
注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作
NSOperationQueue
- NSOperationQueue的作用
1)NSOperation可以调用start方法来执行任务,但默认是同步执行的
2)如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
- 添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
NSOperation和NSOperationQueue的功能代码
自定义的NSoperation:
- 自定义NSOperation的步骤很简单: 重写- (void)main方法,在里面实现想执行的任务
- 重写- (void)main方法的注意点
1) 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
2) 经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
AlanOperation.h
#import <Foundation/Foundation.h>
@interface AlanOperation : NSOperation
@end
AlanOperation.m
@implementation AlanOperation
//告知要执行的任务是什么
//1.有利于代码隐蔽
//2.复用性
-(void)main
{
NSLog(@"main---%@",[NSThread currentThread]);
}
@end
#import "ViewController.h"
#import "AlanOperation.h"
@interface ViewController ()
@end
@implementation ViewController
#pragma mark ----------------------
#pragma mark Events
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self customWithQueue];
}
#pragma mark ----------------------
#pragma mark Methods
-(void)invocationOperationWithQueue
{
//1.创建操作,封装任务
/*
第一个参数:目标对象 self
第二个参数:调用方法的名称
第三个参数:前面方法需要接受的参数 nil
*/
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download2) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download3) object:nil];
//2.创建队列
/*
GCD:
串行类型:create & 主队列
并发类型:create & 全局并发队列
NSOperation:
主队列: [NSOperationQueue mainQueue] 和GCD中的主队列一样,串行队列
非主队列: [[NSOperationQueue alloc]init] 非常特殊(同时具备并发和串行的功能)
//默认情况下,非主队列是并发队列
*/
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//3.添加操作到队列中
[queue addOperation:op1]; //内部已经调用了[op1 start]
[queue addOperation:op2];
[queue addOperation:op3];
}
-(void)blockOperationWithQueue
{
//1.创建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1----%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2----%@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3----%@",[NSThread currentThread]);
}];
//追加任务
[op2 addExecutionBlock:^{
NSLog(@"4----%@",[NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"5----%@",[NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"6----%@",[NSThread currentThread]);
}];
//2.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//3.添加操作到队列
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
//简便方法
//1)创建操作,2)添加操作到队列中
[queue addOperationWithBlock:^{
NSLog(@"7----%@",[NSThread currentThread]);
}];
}
-(void)customWithQueue
{
//1.封装操作
AlanOperation *op1 = [[AlanOperation alloc]init];
AlanOperation *op2 = [[AlanOperation alloc]init];
//2.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//3.添加操作到队列
[queue addOperation:op1];
[queue addOperation:op2];
}
-(void)download1
{
NSLog(@"%s----%@",__func__,[NSThread currentThread]);
}
-(void)download2
{
NSLog(@"%s----%@",__func__,[NSThread currentThread]);
}
-(void)download3
{
NSLog(@"%s----%@",__func__,[NSThread currentThread]);
}
@end
最大并发数
- 什么是并发数
1)同时执行的任务数
2)比如,同时开3个线程执行3个任务,并发数就是3
3)串行执行任务!=只开一条线程 (可以多条线程同步)
maxConcurrentOperationCount >1 那么就是并发队列
maxConcurrentOperationCount == 1 那就是串行队列
maxConcurrentOperationCount == 0 不会执行任务
maxConcurrentOperationCount == -1 特殊意义 最大值 表示不受限制
- 最大并发数的相关方法
1) - (NSInteger)maxConcurrentOperationCount;
2) - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
队列的取消、暂停、恢复
- 取消队列的所有操作
- (void)cancelAllOperations;
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
- 暂停和恢复队列
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended;
队列的并发、取消、暂停、恢复代码
#import "ViewController.h"
@interface ViewController ()
/** 队列 */
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
#pragma mark ----------------------
#pragma mark Events
- (IBAction)startBtnClick:(id)sender
{
//1.创建队列
//默认是并发队列
self.queue = [[NSOperationQueue alloc]init];
//2.设置最大并发数量 maxConcurrentOperationCount
//同一时间最多有多少个任务可以执行
//串行执行任务!=只开一条线程 (线程同步)
// maxConcurrentOperationCount >1 那么就是并发队列
// maxConcurrentOperationCount == 1 那就是串行队列
// maxConcurrentOperationCount == 0 不会执行任务
// maxConcurrentOperationCount == -1 特殊意义 最大值 表示不受限制
self.queue.maxConcurrentOperationCount = 1;
//3.封装操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"1-%zd---%@",i,[NSThread currentThread]);
}
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"2-%zd---%@",i,[NSThread currentThread]);
}
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"3-%zd---%@",i,[NSThread currentThread]);
}
}];
//4.添加到队列
[self.queue addOperation:op1];
[self.queue addOperation:op2];
[self.queue addOperation:op3];
}
- (IBAction)suspendBtnClick:(id)sender
{
//暂停,是可以恢复
/*
队列中的任务也是有状态的:已经执行完毕的 | 正在执行 | 排队等待状态
*/
//不能暂停当前正在处于执行状态的任务
[self.queue setSuspended:YES];
}
- (IBAction)goOnBtnClick:(id)sender
{
//继续执行
[self.queue setSuspended:NO];
}
- (IBAction)cancelBtnClick:(id)sender
{
//取消,不可以恢复
//该方法内部调用了所有操作的cancel方法
[self.queue cancelAllOperations];
}
@end
NSOperation操作依赖和监听
- NSOperation之间可以设置依赖来保证执行顺序
比如一定要让操作A执行完后,才能执行操作B,可以这么写
[operationB addDependency:operationA]; // 操作B依赖于操作A
- 可以在不同queue的NSOperation之间创建依赖关系
注意:不能相互依赖(A依赖B,B依赖A),也不能循环依赖
- 可以监听一个操作的执行完毕
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSOperationQueue *queue2 = [[NSOperationQueue alloc]init];
//2.封装操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1---%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2---%@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3---%@",[NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"4---%@",[NSThread currentThread]);
}];
//操作监听
op3.completionBlock = ^{
NSLog(@"++++客官,来看我吧------%@",[NSThread currentThread]);
};
//添加操作依赖
//注意点:不能循环依赖
//可以跨队列依赖
[op1 addDependency:op4];
// [op4 addDependency:op1];
[op2 addDependency:op3];
//添加操作到队列
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue2 addOperation:op4];
}
@end
NSOperation线程间通信
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self comBie];
}
-(void)download
{
//http://s15.sinaimg.cn/bmiddle/4c0b78455061c1b7f1d0e
//1.开子线程下载图片
//1.1 非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//1.2 封装操作
NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://s15.sinaimg.cn/bmiddle/4c0b78455061c1b7f1d0e"];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
NSLog(@"download---%@",[NSThread currentThread]);
//3.更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
NSLog(@"UI---%@",[NSThread currentThread]);
}];
}];
//2.添加操作到队列
[queue addOperation:download];
}
/*
1.下载图片1
2.下载图片2
3.合并图片
*/
-(void)comBie
{
//1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
__block UIImage *image1;
__block UIImage *image2;
//2 封装操作,下载图片1
NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://s15.sinaimg.cn/bmiddle/4c0b78455061c1b7f1d0e"];
NSData *imageData = [NSData dataWithContentsOfURL:url];
image1 = [UIImage imageWithData:imageData];
NSLog(@"download---%@",[NSThread currentThread]);
}];
//3 封装操作,下载图片2
NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://www.027art.com/feizhuliu/UploadFiles_6650/201109/2011091718442835.jpg"];
NSData *imageData = [NSData dataWithContentsOfURL:url];
image2 = [UIImage imageWithData:imageData];
NSLog(@"download---%@",[NSThread currentThread]);
}];
//4.封装合并图片的操作
NSBlockOperation *combie = [NSBlockOperation blockOperationWithBlock:^{
//4.1 开上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
//4.2 画图1
[image1 drawInRect:CGRectMake(0, 0, 100, 200)];
//4.3 画图2
[image2 drawInRect:CGRectMake(100, 0, 100, 200)];
//4.4 根据上下文得到图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//4.5 关闭上下文
UIGraphicsEndImageContext();
//7.更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
NSLog(@"UI----%@",[NSThread currentThread]);
}];
}];
//5.设置依赖关系
[combie addDependency:download1];
[combie addDependency:download2];
//6.添加操作到队列中
[queue addOperation:download2];
[queue addOperation:download1];
[queue addOperation:combie];
}
@end