1. 简介
NSOperation(任务): 通过start方法开始执行任务、默认是同步执行的
NSOperationQueue(队列): 将NSOperation添加到队列中执行(主队列除外),是异步执行的
2. NSOperation创建
1) NSOperation一般不直接使用,而是使用它的子类NSInvocationOperation和NSBlockOperation
2) 可以看到在没有添加到对列的时候都是在当前线程执行的
文末附工程源码
- (void)createOperation { NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; [op start]; NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); }]; [blockOp start]; } - (void)createOperation { NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; [op start]; NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); }]; [blockOp start]; }
运行结果:
2021-04-11 11:12:24.522251+0800 OperationQueue[1485:43624] obj - <NSThread: 0x600003a0c900>{number = 1, name = main} 2021-04-11 11:12:24.522431+0800 OperationQueue[1485:43624] block - <NSThread: 0x600003a0c900>{number = 1, name = main}
3. 将NSOperation添加到Queue中
1)可以利用[queue addOperation:op]将NSOperation添加到NSOperationQueue中
2)也可以利用[queue addOperations:@[op, blockOp] waitUntilFinished:NO]将NSOperation添加到NSOperationQueue中
3)注意一不可用重复添加同一个NSOperation到NSOperationQueue中,否则会报如下错误
- (void)addToQueue { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; // [op start];//调用start之后就不可以再入队 NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); }]; //添加单个 [queue addOperation:op]; [queue addOperation:blockOp]; //添加一组 [queue addOperations:@[op, blockOp] waitUntilFinished:NO]; // [op start]; // [op cancel]; } - (void)addToQueue { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; // [op start];//调用start之后就不可以再入队 NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); }]; //添加单个 [queue addOperation:op]; [queue addOperation:blockOp]; //添加一组 [queue addOperations:@[op, blockOp] waitUntilFinished:NO]; // [op start]; // [op cancel]; }
运行结果:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperations:waitUntilFinished:]: 2 (of 2) operations are finished, executing, or already in a queue, and cannot be enqueued' terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperations:waitUntilFinished:]: 2 (of 2) operations are finished, executing, or already in a queue, and cannot be enqueued' terminating with uncaught exception of type NSException
4)注意二不可将NSOperation调用start或者cancel之后再添加到NSOperationQueue中,否则会报如下错误
- (void)addToQueue { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; [op start];//调用start之后就不可以再入队 NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); }]; //添加单个 [queue addOperation:op]; [queue addOperation:blockOp]; //添加一组 // [queue addOperations:@[op, blockOp] waitUntilFinished:NO]; // [op start]; // [op cancel]; }
运行结果:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperation:]: operation is finished and cannot be enqueued' terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperation:]: operation is finished and cannot be enqueued' terminating with uncaught exception of type NSException
5)注意三不可将NSOperation添加NSOperationQueue之后,再次调用star或者cancelt去修改状态,否则会报如下错误
- (void)addToQueue { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; // [op start];//调用start之后就不可以再入队 NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); }]; //添加单个 [queue addOperation:op]; [queue addOperation:blockOp]; //添加一组 // [queue addOperations:@[op, blockOp] waitUntilFinished:NO]; [op start]; // [op cancel]; } - (void)addToQueue { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; // [op start];//调用start之后就不可以再入队 NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); }]; //添加单个 [queue addOperation:op]; [queue addOperation:blockOp]; //添加一组 // [queue addOperations:@[op, blockOp] waitUntilFinished:NO]; [op start]; // [op cancel]; }
运行结果:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSInvocationOperation start]: something is trying to start the receiver simultaneously from more than one thread' terminating with uncaught exception of type NSException
4. 添加依赖关系
1)当需要某个NSOperation对象依赖于其它NSOperation对象时,可以通过addDependency方法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作的时候,当前NSOperation对象才会开始执行操作,也可以通过removeDependency方法来删除依赖对象
2)不添加依赖关系应该先打印block再打印obj见上方日志,设置依赖后见下方代码说明
3)设置依赖关系,必须在addOperation之前设置,否则没有效果
4)不可以相互依赖,否则都不会执行
- (void)addDependency { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); }]; [op addDependency:blockOp];//添加依赖 // [blockOp addDependency:op];//相互依赖 // [op removeDependency:blockOp];//移除依赖 [queue addOperation:op]; [queue addOperation:blockOp]; } - (void)addDependency { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); }]; [op addDependency:blockOp];//添加依赖 // [blockOp addDependency:op];//相互依赖 // [op removeDependency:blockOp];//移除依赖 [queue addOperation:op]; [queue addOperation:blockOp]; }
运行结果:
2021-04-11 11:29:47.303916+0800 OperationQueue[1586:56394] block - <NSThread: 0x600003ead180>{number = 5, name = (null)} 2021-04-11 11:29:47.304047+0800 OperationQueue[1586:56394] obj - <NSThread: 0x600003ead180>{number = 5, name = (null)} 2021-04-11 11:29:47.303916+0800 OperationQueue[1586:56394] block - <NSThread: 0x600003ead180>{number = 5, name = (null)} 2021-04-11 11:29:47.304047+0800 OperationQueue[1586:56394] obj - <NSThread: 0x600003ead180>{number = 5, name = (null)}
5. 优先级
1)执行顺序规则
不设置依赖关系的前提下:(可通过调试下方代码看执行结果)
(1)如果都不设置优先级、并且内部没有耗时操作,则按照添加顺序执行;
(2)如果都不设置优先级、并且内部有耗时操作,则耗时操作的后执行,没有耗时操作的先执行;
(3)如果设置了优先级、并且内部没有耗时操作,则按照处于ready状态的operation优先级等级的高低执行;
(4)如果设置了优先级、并且内部有耗时操作,则耗时操作的后执行,没有耗时操作的按照处于ready状态的operation优先级等级的高低执行;
2)优先级不能替代依赖关系并且依赖关系高于优先级的设置,在保证依赖关系的情况下才能考虑优先级
- (void)setQueuePriority { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); sleep(3); }]; [op setQueuePriority:NSOperationQueuePriorityHigh]; [blockOp setQueuePriority:NSOperationQueuePriorityLow]; [op addDependency:blockOp]; [queue addOperation:op]; [queue addOperation:blockOp]; } - (void)setQueuePriority { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"]; NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block - %@", [NSThread currentThread]); sleep(3); }]; [op setQueuePriority:NSOperationQueuePriorityHigh]; [blockOp setQueuePriority:NSOperationQueuePriorityLow]; [op addDependency:blockOp]; [queue addOperation:op]; [queue addOperation:blockOp]; }
运行结果:
2021-04-11 11:39:07.960943+0800 OperationQueue[1621:62599] block - <NSThread: 0x600000715640>{number = 3, name = (null)} 2021-04-11 11:39:10.964436+0800 OperationQueue[1621:62599] obj - <NSThread: 0x600000715640>{number = 3, name = (null)} 2021-04-11 11:39:07.960943+0800 OperationQueue[1621:62599] block - <NSThread: 0x600000715640>{number = 3, name = (null)} 2021-04-11 11:39:10.964436+0800 OperationQueue[1621:62599] obj - <NSThread: 0x600000715640>{number = 3, name = (null)}
6. NSOperationQueue的挂起和恢复
可以根据你实际的需求来确定是否要挂起和恢复,挂起或者恢复NSOperationQueue,并不会影响正在执行的NSOperation
[queue setSuspended:YES];//挂起 [queue setSuspended:NO];//恢复 [queue setSuspended:YES];//挂起 [queue setSuspended:NO];//恢复
7. 等待完成
1) [blockOp waitUntilFinished]等待当前的操作完成后,才会继续向下执行,注意:会阻塞线程
2) [queue waitUntilAllOperationsAreFinished]等待所有的操作完成后,才会继续向下执行,注意:会阻塞线程
[blockOp waitUntilFinished]; [queue waitUntilAllOperationsAreFinished]; [blockOp waitUntilFinished]; [queue waitUntilAllOperationsAreFinished];
8. 取消Operations
取消所有正在执行的操作
[queue cancelAllOperations];// 两个操作都不会执行
9. 并发数
设置单次最大的执行数量,如果多于1个,是完成一个向队列里面追加一个的执行,而不是等待同时执行的最大个数执行完之后再执行后面的操作
queue.maxConcurrentOperationCount = 1;
10. 自定义NSOperation
1)NSInvocationOperation和NSBlockOperation不能满足我们的需求时就需要我们自定义,自定义NSOperation的场景主要是为了我们能更好的控制一个任务,什么情况下开始、取消、完成, 并且可以分离代码的实现
2)实现步骤:(可参考下面代码)
(1)实现start方法
(2)重写isAsynchronous,是否为并发任务、根据自己的情况即可
(3)main方法可根据自己的实际需要决定是否实现、一般实现并在里面做自己的事
(4)finished、executing更新任务的状态,用于触发KVO
(5)自己的任务完成后,一定要把当前的任务状态改为完成,否则队列中后面的任务,将永远不会执行
@property(assign, nonatomic, getter=isFinished) BOOL finished; @property(assign, nonatomic, getter=isExecuting) BOOL executing; @property(assign, nonatomic, getter=isStart) BOOL start; @property(assign, nonatomic, getter=isFinished) BOOL finished; @property(assign, nonatomic, getter=isExecuting) BOOL executing; @property(assign, nonatomic, getter=isStart) BOOL start;
@implementation CustomOperation @synthesize finished = _finished, executing = _executing; - (BOOL)isAsynchronous { return NO; } - (void)setFinished:(BOOL)finished { [self willChangeValueForKey:@"finished"]; _finished = finished; [self didChangeValueForKey:@"finished"]; } - (void)setExecuting:(BOOL)executing { [self willChangeValueForKey:@"executing"]; _executing = executing; [self didChangeValueForKey:@"executing"]; } - (void)start { @synchronized (self) { self.start = YES; if (self.isCancelled) { [self completed]; return; } @autoreleasepool { [self main]; } self.executing = YES; } } - (void)main {//在此处做一些更复杂的操作 NSLog(@"准备开始执行任务啦~ %@", [NSThread currentThread]); //模拟3s后执行doSomething [self performSelector:@selector(doSomething:) withObject:nil afterDelay:3.0]; [[NSRunLoop currentRunLoop] run];//此处需要添加到Runloop,子线程默认没有开启,后期文章会具体说明 } - (void)doSomething:(id)obj { NSLog(@"完成了当前的任务!%@", [NSThread currentThread]); [self completed]; } - (void)completed { @synchronized (self) { self.executing = NO; self.finished = YES; } } - (void)cancel { [super cancel]; if (self.isStart) { [self completed]; } } @end @implementation CustomOperation @synthesize finished = _finished, executing = _executing; - (BOOL)isAsynchronous { return NO; } - (void)setFinished:(BOOL)finished { [self willChangeValueForKey:@"finished"]; _finished = finished; [self didChangeValueForKey:@"finished"]; } - (void)setExecuting:(BOOL)executing { [self willChangeValueForKey:@"executing"]; _executing = executing; [self didChangeValueForKey:@"executing"]; } - (void)start { @synchronized (self) { self.start = YES; if (self.isCancelled) { [self completed]; return; } @autoreleasepool { [self main]; } self.executing = YES; } } - (void)main {//在此处做一些更复杂的操作 NSLog(@"准备开始执行任务啦~ %@", [NSThread currentThread]); //模拟3s后执行doSomething [self performSelector:@selector(doSomething:) withObject:nil afterDelay:3.0]; [[NSRunLoop currentRunLoop] run];//此处需要添加到Runloop,子线程默认没有开启,后期文章会具体说明 } - (void)doSomething:(id)obj { NSLog(@"完成了当前的任务!%@", [NSThread currentThread]); [self completed]; } - (void)completed { @synchronized (self) { self.executing = NO; self.finished = YES; } } - (void)cancel { [super cancel]; if (self.isStart) { [self completed]; } } @end
运行结果:
2021-04-11 11:48:12.601640+0800 OperationQueue[1671:69134] 准备开始执行任务啦~ <NSThread: 0x600000b58380>{number = 8, name = (null)}2021-04-11 11:48:15.605299+0800 OperationQueue[1671:69134] 完成了当前的任务!<NSThread: 0x600000b58380>{number = 8, name = (null)}