NSOperation
- NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。
- NSOperation的子类
- NSInvocationOperation
- NSBlockOperation
- 自定义子类继承NSOperation,实现内部相应的方法
关于NSInvocationOperation
- 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
- 只有NSInvocationOperation放到一个NSOperationQueue中,才会异步执行
- (IBAction)invocationOperation:(id)sender {
//初始化Operation子类
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
//调用start方法开始执行操作,一旦执行操作就会调用run方法
[operation start];
}
-(void)run{
NSLog(@"0--%@",[NSThread currentThread]);
}
invocationOperation.png
关于NSBlockOperation
- 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
- 只有NSBlockOperation放到一个NSOperationQueue中,才会异步执行
- (IBAction)blockOperation:(id)sender {
//初始化Operation子类
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
//在主线程
NSLog(@"1--%@",[NSThread currentThread]);
}];
[operation start];
}
blockOperationlog.png
- NSBlockOperation 通过 addExecutionBlock: 就可以为 NSBlockOperation 添加额外的操作。这些操作(包括 blockOperationWithBlock 中的操作)可以在不同的线程中同时(并发)执行
- 只要NSBlockOperation封装的操作数大于1,就会异步执行
注: 如果添加的操作多的话,blockOperationWithBlock: 中的操作也可能会在其他线程(非当前线程)中执行,这是由系统决定的,并不是说添加到 blockOperationWithBlock: 中的操作一定会在当前线程中执行
代码语言:javascript复制- (IBAction)blockOperation:(id)sender {
//初始化Operation子类
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
//在主线程
NSLog(@"1--%@",[NSThread currentThread]);
}];
//添加额外的任务(在子线程执行)
[operation addExecutionBlock:^{
NSLog(@"2--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"3--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"4--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"5--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"6--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"7--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"8--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"9--%@",[NSThread currentThread]);
}];
[operation start];
}
blockOperationlog.png
自定义子类继承NSOperation
- 使用自定义子类继承NSOperation,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
- 通过重写 main(非并发) 或者 start(并发) 方法来定义自己的 NSOperation 对象
- 重写main方法比较简单,不需要管理操作的状态属性 isExecuting 和 isFinished
- 当 main 执行完返回的时候,这个操作就结束了
#import <Foundation/Foundation.h>
@interface SSOperation : NSOperation
@end
#import "SSOperation.h"
@implementation SSOperation
#pragma mark-需要执行的任务
-(void)main{
if (!self.isCancelled) {
NSLog(@"0--%@",[NSThread currentThread]);
}
}
@end
- (IBAction)SSOperationOperation:(id)sender {
SSOperation *operation = [[SSOperation alloc]init];
[operation start];
}
SSOperationlog.png
关于NSOperationQueue
NSOperationQueue 一共有两种队列:主队列、自定义队列。其中自定义队列同时包含了串行、并发功能。下边是主队列、自定义队列的基本创建方法和特点
主队列
代码语言:javascript复制- (IBAction)creatOperationQueue1:(id)sender {
//主队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
//创建操作(任务)
//创建--NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run2) object:nil];
//创建--NSBlockOperation
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"-1--%@",[NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"-2--%@",[NSThread currentThread]);
}];
//自定义(需要继承NSOperation,执行的操作需要放在这个自定义类的main中)
SSOperation *op5 = [[SSOperation alloc]init];
//添加任务队列中
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
//也可以直接创建任务到队列中去
[queue addOperationWithBlock:^{
NSLog(@"-3--%@",[NSThread currentThread]);
}];
}
-(void)run1{
NSLog(@"-run1--%@",[NSThread currentThread]);
}
-(void)run2{
NSLog(@"-run2--%@",[NSThread currentThread]);
}
主队列log.png
自定义队列(系统会自动异步执行NSOperation中的操作)
- 使用 NSOperation 子类创建操作,并使用 addOperation: 将操作加入到操作队列后能够开启新线程,进行并发执行
- (IBAction)creatOperationQueue1:(id)sender {
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//创建操作(任务)
//创建--NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run2) object:nil];
//创建--NSBlockOperation
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"-1--%@",[NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"-2--%@",[NSThread currentThread]);
}];
//自定义队列(需要继承NSOperation,执行的操作需要放在这个自定义类的main中)
SSOperation *op5 = [[SSOperation alloc]init];
//添加任务队列中
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
//也可以直接创建任务到队列中去
[queue addOperationWithBlock:^{
NSLog(@"-3--%@",[NSThread currentThread]);
}];
}
-(void)run1{
NSLog(@"-run1--%@",[NSThread currentThread]);
}
-(void)run2{
NSLog(@"-run2--%@",[NSThread currentThread]);
}
自定义队列log.png
NSOperationQueue设置最大并发操作数
- 通过属性maxConcurrentOperationCount,叫做最大并发操作数。用来控制一个特定队列中可以有多少个操作同时参与并发执行
- 这里 maxConcurrentOperationCount 控制的不是并发线程的数量,而是一个队列中同时能并发执行的最大操作数。而且一个操作也并非只能在一个线程中运行
- maxConcurrentOperationCount 默认情况下为-1,表示不进行限制,可进行并发执行。
- maxConcurrentOperationCount 为1时,队列为串行队列。只能串行执行。
- maxConcurrentOperationCount 大于1时,队列为并发队列。操作并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整为 min{自己设定的值,系统设定的默认最大值}
//maxConcurrentOperationCount = 1
- (IBAction)creatOperationQueue2:(id)sender {
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//设置为1就成了串行队列
queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
NSLog(@"-1--%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"-2--%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"-3--%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"-4--%@",[NSThread currentThread]);
}];
}
maxConcurrentOperationCount = 1
代码语言:javascript复制//maxConcurrentOperationCount = 2
- (IBAction)creatOperationQueue2:(id)sender {
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//设置最大并发操作数(不管加入队列有多少操作,实际队列并发数为2)
queue.maxConcurrentOperationCount = 2;
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"-1--%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"-2--%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"-3--%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"-4--%@",[NSThread currentThread]);
}];
}
maxConcurrentOperationCount = 2
NSOperationQueue设置队列挂起与取消
- 当队列调用了队列挂起的方法( self.queue.suspended = YES),队列里的执行方法立即停止,但是有一点需要注意的是,当block操作中,队列挂起是不起作用的,它是无法停止的,必须操作执行结束后才会生效。
- 当队列调用取消( [self.queue cancelAllOperations])就意味着后续队列不再执行,再次启动需要重新加入队列
- (IBAction)createOperationQueueSuspended:(id)sender {
//创建队列
self.queue = [[NSOperationQueue alloc]init];
self.queue.maxConcurrentOperationCount = 1;
[self.queue addOperationWithBlock:^{
NSLog(@"-1--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
[self.queue addOperationWithBlock:^{
NSLog(@"-2--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
[self.queue addOperationWithBlock:^{
NSLog(@"-3--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
[self.queue addOperationWithBlock:^{
NSLog(@"-4--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//设置队列挂起或者取消的话都必须是在block方法执行完之后才有效
[self.queue addOperationWithBlock:^{
for(NSInteger i = 0;i<5;i ){
NSLog(@"-5--%zd---%@",(long)i,[NSThread currentThread]);
}
}];
}
#pragma mark-设置队列挂起
- (IBAction)operationSetSuspended:(id)sender {
if(self.queue.suspended){
//恢复队列,继续执行
self.queue.suspended = NO;
}else{
//挂起(暂停队列)
self.queue.suspended = YES;
}
}
#pragma mark-设置队列取消(取消就意味着后续队列不再执行,再次启动需要重新加入队列)
- (IBAction)operationSetCancel:(id)sender {
[self.queue cancelAllOperations];
}
NSOperationQueue设置队列依赖
代码语言:javascript复制- (IBAction)createOperationQueueSetDependency:(id)sender {
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down1---%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down2---%@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down3---%@",[NSThread currentThread]);
}];
//设置依赖(op1和op3执行完之后才执行2)
[op2 addDependency:op1];
[op2 addDependency:op3];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
//监听一个操作的执行完成
[op2 setCompletionBlock:^{
NSLog(@"执行完成");
}];
}
注:一定要避免相互依赖,比如
代码语言:javascript复制[op3 addDependency:op1];
[op1 addDependency:op3]; //错误的写法---相互依赖
设置队列依赖.png
监控NSOperation对象的属性
- isExecuting 代表任务正在执行中
- isFinished 代表任务已经执行完成,被取消也算执行完成 注:该状态关系到依赖其的操作任务,只有在其isFinished状态为YES的时候,依赖其的操作任务才能开始执行,操作队列也是根据这个状态来决定是否将操作任务从队列中移除
- isCancelled 代表任务已经取消执行
- isAsynchronous 代表任务是并发还是同步执行, 注:当操作任务加入到操作队列后,会忽略该属性
- isReady 代表任务是否已经准备执行 注:当其依赖的操作任务都执行完时,改状态才会是YES
NSOperation在队列里的优先级
- iOS8以前,NSOperation通过设置queuePriority属性来设置优先级
- iOS 8.0后,NSOperation通过设置qualityOfService来设置优先级
- 优先级高的先执行,低的后执行
//iOS8以前的优先级(queuePriority)
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
//iOS8以后的优先级(qualityOfService)
typedef NS_ENUM(NSInteger, NSQualityOfService) {
NSQualityOfServiceUserInteractive = 0x21,//最高优先级,用于用户交互事件
NSQualityOfServiceUserInitiated = 0x19,//次高优先级,用于用户需要马上执行的事件
NSQualityOfServiceUtility = 0x11,//默认优先级,主线程和没有设置优先级的线程都默认为这个优先级
NSQualityOfServiceBackground = 0x09,//普通优先级,用于普通任务
NSQualityOfServiceDefault = -1//最低优先级,用于不重要的任务
}
代码语言:javascript复制 NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down1---%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down2---%@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down3---%@",[NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down4---%@",[NSThread currentThread]);
}];
op3.qualityOfService = NSQualityOfServiceUserInteractive;
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
优先级.png