玩转iOS开发:iOS中的NSOperation开发(二)

926 阅读8分钟

文章分享至我的个人技术博客: https://cainluo.github.io/15019907895337.html


在上一章, 我们把NSOperation的一些基础概念和一些简单的用法给摸了一遍, 但在示例当中, 我们发现了只单独使用NSOperation的话, 只会在主线程里执行, 虽然NSBlockOperation额外可以添加任务在子线程里执行, 但这还是不够滴, 这次我们接下来就把NSOperationNSOperationQueue配合使用给补全.

如果没有看过上一篇文章的话, 可以去看看玩转iOS开发:iOS中的NSOperation开发(一).

转载声明:如需要转载该文章, 请联系作者, 并且注明出处, 以及不能擅自修改本文.


NSOperationQueue

NSOperationQueueGCD的队列有些区别, 在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)}
  • 看过上一篇文章的人都知道, 直接使用NSInvocationOperationNSBlockOperation(不使用添加额外任务的方法)只会在主线程中执行, 但如果配合着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的时候, 可以看得出是串行执行(并行执行这里就不演示了)的, 而且开启的线程数也是由系统所决定的.

任务与任务之间的依赖关系

NSOperationNSOperationQueue中, 个人觉得最吸引人的地方, 还是这个依赖关系, 它可以让指定的任务依赖于另一个任务, 只有当被依赖的任务执行完之后, 才会去执行依赖的任务, 说的有些拗口, 直接来看代码吧:

- (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;
  • 设置NSOperationQueuesuspended属性, 可以暂停和恢复任务的操作, YES代表暂停队列, NO代表恢复队列.
@property (getter=isSuspended) BOOL suspended;
  • NSOperationQueueisSuspended可以判断任务的操作是暂停还是恢复.

注意: 这里的暂停任务操作, 指的不是将当前的操作立刻取消, 而是当当前这个任务执行完之后, 就不再执行了. 暂停是暂时暂停, 之后可以恢复, 而取消指的是取消所有操作, 就没法再接着执行剩下的操作.


总结

好了, 关于NSOperation的相关知识, 这里差不多就讲完了, 如果想看更详细的, 可以去看看NSOperation官方文档, NSOperationQueue官方文档.


工程地址

项目地址: https://github.com/CainRun/iOS-Project-Example/tree/master/NSOperationQueue-Example


最后

码字很费脑, 看官赏点饭钱可好

微信

支付宝