文章分享至我的个人技术博客: https://cainluo.github.io/15019907895337.html
在上一章, 我们把NSOperation的一些基础概念和一些简单的用法给摸了一遍, 但在示例当中, 我们发现了只单独使用NSOperation的话, 只会在主线程里执行, 虽然NSBlockOperation额外可以添加任务在子线程里执行, 但这还是不够滴, 这次我们接下来就把NSOperation和NSOperationQueue配合使用给补全.
如果没有看过上一篇文章的话, 可以去看看玩转iOS开发:iOS中的NSOperation开发(一).
转载声明:如需要转载该文章, 请联系作者, 并且注明出处, 以及不能擅自修改本文.
NSOperationQueue
NSOperationQueue和GCD的队列有些区别, 在NSOperationQueue里, 分为主队列和其他队列, 而GCD是有并行队列, 串行队列.
在NSOperationQueue中的其他队列, 具有并行和串行功能, 那么我们就来看看它们是怎么耍的:
创建主队列:
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
- 凡是添加到主队列执行的任务, 都会放到主线程去执行.
创建其他队列:
NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init];
- 添加到其他队列里的任务, 就会自动放到子线程去执行, 这里包括了并行和串行两种方式.
添加任务到NSOperationQueue中
前面我们也提到了, NSOperation需要配合着NSOperationQueue来实现多线程操作, 那么这里就有两种实现的方式.
第一种方式
使用下面这个方法来将任务添加到队列中:
- (void)addOperation:(NSOperation *)op;
- (void)addMissionToOperationQueue {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(runInvocationOperation)
object:nil];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i < 2; i++) {
NSLog(@"blockOperation执行第%zd任务, 当前的线程为: %@", i, [NSThread currentThread]);
}
}];
[queue addOperation:invocationOperation];
[queue addOperation:blockOperation];
}
- (void)runInvocationOperation {
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"invocationOperation执行第%zd任务, 当前的线程为: %@", i, [NSThread currentThread]);
}
}
2017-08-06 12:06:53.950 NSOperationQueue-Example[2325:157835] invocationOperation执行第0任务, 当前的线程为: <NSThread: 0x60000006c540>{number = 3, name = (null)}
2017-08-06 12:06:53.950 NSOperationQueue-Example[2325:157825] blockOperation执行第0任务, 当前的线程为: <NSThread: 0x608000074180>{number = 4, name = (null)}
2017-08-06 12:06:53.955 NSOperationQueue-Example[2325:157835] invocationOperation执行第1任务, 当前的线程为: <NSThread: 0x60000006c540>{number = 3, name = (null)}
2017-08-06 12:06:53.989 NSOperationQueue-Example[2325:157825] blockOperation执行第1任务, 当前的线程为: <NSThread: 0x608000074180>{number = 4, name = (null)}
2017-08-06 12:06:53.990 NSOperationQueue-Example[2325:157835] invocationOperation执行第2任务, 当前的线程为: <NSThread: 0x60000006c540>{number = 3, name = (null)}
- 看过上一篇文章的人都知道, 直接使用
NSInvocationOperation和NSBlockOperation(不使用添加额外任务的方法)只会在主线程中执行, 但如果配合着NSOperationQueue来使用的话, 就可以自动开启子线程, 并且是同步执行.
第二种方式
使用下面这个方法来将任务添加到队列中:
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);
- (void)addMissionToPerationQueueBlock {
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperationWithBlock:^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"执行第%zd任务, 当前的线程为: %@", i, [NSThread currentThread]);
}
}];
}
2017-08-06 12:13:16.518 NSOperationQueue-Example[2382:165154] 执行第0任务, 当前的线程为: <NSThread: 0x60000007c140>{number = 3, name = (null)}
2017-08-06 12:13:16.519 NSOperationQueue-Example[2382:165154] 执行第1任务, 当前的线程为: <NSThread: 0x60000007c140>{number = 3, name = (null)}
2017-08-06 12:13:16.519 NSOperationQueue-Example[2382:165154] 执行第2任务, 当前的线程为: <NSThread: 0x60000007c140>{number = 3, name = (null)}
- 从结果中我们可以看得出, 这个方法更加的简洁代码, 而且也是可以开启新线程并且同步执行任务的.
注意: 这里还需要注意一下, 将任务添加到队列中的时候, 我们不需要手动去调用- (void)star;方法, 队列会自动去调用.
自由切换串行/并行队列
刚刚我们演示的都是并行队列的操作方式, 那如果要求是串行任务咋办咧? 这时候我们只需要更改一个东西就可以了:
@property NSInteger maxConcurrentOperationCount;
- (void)changeSyncOrAsyncQueue {
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
// 如果要串行队列, 就设置为1, 否则就不设置, 默认并行队列
operationQueue.maxConcurrentOperationCount = 1;
[operationQueue addOperationWithBlock:^{
NSLog(@"执行第一个任务, 当前的线程为: %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.1];
}];
[operationQueue addOperationWithBlock:^{
NSLog(@"执行第二个任务, 当前的线程为: %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.1];
}];
[operationQueue addOperationWithBlock:^{
NSLog(@"执行第三个任务, 当前的线程为: %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.1];
}];
[operationQueue addOperationWithBlock:^{
NSLog(@"执行第四个任务, 当前的线程为: %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.1];
}];
[operationQueue addOperationWithBlock:^{
NSLog(@"执行第五个任务, 当前的线程为: %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.1];
}];
}
2017-08-06 12:24:24.786 NSOperationQueue-Example[2571:179165] 执行第一个任务, 当前的线程为: <NSThread: 0x6000000771c0>{number = 3, name = (null)}
2017-08-06 12:24:24.887 NSOperationQueue-Example[2571:179162] 执行第二个任务, 当前的线程为: <NSThread: 0x600000076840>{number = 4, name = (null)}
2017-08-06 12:24:24.992 NSOperationQueue-Example[2571:179165] 执行第三个任务, 当前的线程为: <NSThread: 0x6000000771c0>{number = 3, name = (null)}
2017-08-06 12:24:25.096 NSOperationQueue-Example[2571:179162] 执行第四个任务, 当前的线程为: <NSThread: 0x600000076840>{number = 4, name = (null)}
2017-08-06 12:24:25.200 NSOperationQueue-Example[2571:179162] 执行第五个任务, 当前的线程为: <NSThread: 0x600000076840>{number = 4, name = (null)}
- 从上面的结果可以看得出, 当我们把最大并发数设置为
1的时候, 可以看得出是串行执行(并行执行这里就不演示了)的, 而且开启的线程数也是由系统所决定的.
任务与任务之间的依赖关系
在NSOperation和NSOperationQueue中, 个人觉得最吸引人的地方, 还是这个依赖关系, 它可以让指定的任务依赖于另一个任务, 只有当被依赖的任务执行完之后, 才会去执行依赖的任务, 说的有些拗口, 直接来看代码吧:
- (void)addDependency {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *blockOperationOne = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行第一次任务, 当前线程为: %@", [NSThread currentThread]);
}];
NSBlockOperation *blockOperationTwo = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行第二次任务, 当前线程为: %@", [NSThread currentThread]);
}];
[blockOperationTwo addDependency:blockOperationOne];
[queue addOperation:blockOperationOne];
[queue addOperation:blockOperationTwo];
}
2017-08-06 13:34:38.095 NSOperationQueue-Example[2962:210009] 执行第一次任务, 当前线程为: <NSThread: 0x600000075b80>{number = 3, name = (null)}
2017-08-06 13:34:38.110 NSOperationQueue-Example[2962:210009] 执行第二次任务, 当前线程为: <NSThread: 0x600000075b80>{number = 3, name = (null)}
- 从结果上来看, 是执行完第一个任务之后, 才会去执行第二个任务, 无论执行多少次, 或者是延迟多少次都一个样.
- 而且, 就算第二个任务先加入队列, 也是会先执行第一个任务, 才会执行第二个任务.
NSOperation的其他方法
- 可以取消
NSOperation的单个操作.
- (void)cancel;
NSOperationQueue提供的方法, 可以取消队列中所有的操作
- (void)cancelAllOperations;
- 设置
NSOperationQueue的suspended属性, 可以暂停和恢复任务的操作,YES代表暂停队列,NO代表恢复队列.
@property (getter=isSuspended) BOOL suspended;
NSOperationQueue的isSuspended可以判断任务的操作是暂停还是恢复.
注意: 这里的暂停任务操作, 指的不是将当前的操作立刻取消, 而是当当前这个任务执行完之后, 就不再执行了. 暂停是暂时暂停, 之后可以恢复, 而取消指的是取消所有操作, 就没法再接着执行剩下的操作.
总结
好了, 关于NSOperation的相关知识, 这里差不多就讲完了, 如果想看更详细的, 可以去看看NSOperation官方文档, NSOperationQueue官方文档.
工程地址
项目地址: https://github.com/CainRun/iOS-Project-Example/tree/master/NSOperationQueue-Example