ios多线程操作(十)—— 用NSOperation开启多线程操作

     想要使用NSOperation(操作)来开启多线程操作必须配合NSOperationQueue(队列)来实现。NSOperation中并没有“任务”这个概念,取而代之的是“操作”,操作中封装着需要在子线程上执行的代码。具体的实现步骤如下:
     1、先将需要执行的操作封装到一个NSOperation对象中
     2、然后将NSOperation对象添加到NSOperationQueue中
     3、系统会自动将NSOperationQueue中的NSOperation取出来
     4、将取出的NSOperation封装的操作放到一条新线程中执行
     由于NSoperation是个抽象类,并不具备封装操作的能力,所以必须使用它的子类。使用NSoperation子类的方式有3种:
     1、NSInvocationOperation
     2、NSBlockOperation
     3、自定义子类继承NSoperation,实现内部相应地方法。
下面分别来探究一个NSoperation的子类!
     NSInvocationOperation
创建NSInvocationOperation对象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:(id) selector:(SEL) object:(id)]

需要传入一个方法SEL和该方法的参数id,并调用该操作的start方法才能执行。默认情况下调用了start方法后并不会开一条新线程区执行操作,而是在当前线程同步执行操作,如下:
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"Invocation"];
   [op start];

- (void)downloadImage:(id)obj {
    NSLog(@"%@ - %@", [NSThread currentThread], obj);
}

该方法执行效果如下
技术分享

当我们将操作添加到队列时,去掉start方法,添加如下代码:
// 将操作添加到队列 -> 异步执行 @selector 方法
    NSOperationQueue *q = [[NSOperationQueue alloc] init];
    [q addOperation:op];


运行结果如下:
技术分享

此时已经开启了子线程!

另一种定义操作的方式——使用NSBlockOperation,该对象的创建如下:
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
  
        }]

该方法用一个block包装着要执行的代码返回一个操作,功能与NSInvocation相同,同样可以添加到队列中,例如
NSOperationQueue *q = [[NSOperationQueue alloc] init];
    
    for (int i = 0; i < 10; ++i) {
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@(i)];
       // 将操作添加到队列
        [q addOperation:op];
    }

该方法将执行代码与操作写在一起,便于进行维护(推荐使用该方法)
运行结果如下:
技术分享

我们可以看到后台此时竟然开启了10条子线程!!!为了解决这个问题,NSOperationQueue提供了一个maxConcurrentOperationCount的属性,可以设置最大并发数,例如我们将代码改写如下:
 NSOperationQueue *q = [[NSOperationQueue alloc] init];
    q.maxConcurrentOperationCount = 2;
    
    for (int i = 0; i < 10; ++i) {
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"睡1秒");
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"%@ - %d", [NSThread currentThread], i);
        }];
        
        [q addOperation:op];
    }

运行效果如下:
技术分享
可以看到线程数明显减少了,而且是两个两个并发执行的。
maxConcurrentOperationCount该参数设置的是最大并发数,也就是说只能同时执行两条线程,至于队列下次从线程缓冲池中取出的是哪条线程我们不得而知,而且我们也不用线程!
现在我们来取消一下最大并发数,也就是注释掉这句代码
q.maxConcurrentOperationCount = 2;
那么执行结果会如下:
技术分享

控制台会瞬间打印出”睡1秒“,然后停顿1秒之后再几乎同时打印接下来的内容!
由此我们可以得知在没有指定最大并发数时有多少个操作添加到队列中就会开启多少条子线程,这得浪费多少资源啊!!!

     使用NSOperation同样能够实现线程间通讯,如下
[self.queue addOperationWithBlock:^{
        NSLog(@"耗时的操作 %@", [NSThread currentThread]);
       
        // 在主线程更新 UI
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"更新 UI %@", [NSThread currentThread]);
        }];
    }];

执行效果如下:
技术分享

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。