想要使用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对象
[objc] view plain copy
- NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:(id) selector:(SEL) object:(id)]
需要传入一个方法SEL和该方法的参数id,并调用该操作的start方法才能执行。默认情况下调用了start方法后并不会开一条新线程区执行操作,而是在当前线程同步执行操作,如下:
[objc] view plain copy
1. NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"Invocation"];
2. start];
[objc] view plain copy
1. - (void)downloadImage:(id)obj {
2. @"%@ - %@", [NSThread currentThread], obj);
3. }
该方法执行效果如下
当我们将操作添加到队列时,去掉start方法,添加如下代码:
[objc] view plain copy
1. // 将操作添加到队列 -> 异步执行 @selector 方法
2. NSOperationQueue *q = [[NSOperationQueue alloc] init];
3. addOperation:op];
运行结果如下:
此时已经开启了子线程!
另一种定义操作的方式——使用NSBlockOperation,该对象的创建如下:
[objc] view plain copy
1. NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
2.
3. }]
该方法用一个block包装着要执行的代码返回一个操作,功能与NSInvocation相同,同样可以添加到队列中,例如
[objc] view plain copy
1. NSOperationQueue *q = [[NSOperationQueue alloc] init];
2.
3. for (int i = 0; i < 10; ++i) {
4. NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@(i)];
5. // 将操作添加到队列
6. addOperation:op];
7. }
该方法将执行代码与操作写在一起,便于进行维护(推荐使用该方法)
运行结果如下:
我们可以看到后台此时竟然开启了10条子线程!!!为了解决这个问题,NSOperationQueue提供了一个maxConcurrentOperationCount的属性,可以设置最大并发数,例如我们将代码改写如下:
[objc] view plain copy
1. NSOperationQueue *q = [[NSOperationQueue alloc] init];
2. .maxConcurrentOperationCount = 2;
3.
4. for (int i = 0; i < 10; ++i) {
5. NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
6. @"睡1秒");
7. sleepForTimeInterval:1.0];
8. @"%@ - %d", [NSThread currentThread], i);
9. }];
10.
11. addOperation:op];
12. }
运行效果如下:
可以看到线程数明显减少了,而且是两个两个并发执行的。
maxConcurrentOperationCount该参数设置的是最大并发数,也就是说只能同时执行两条线程,至于队列下次从线程缓冲池中取出的是哪条线程我们不得而知,而且我们也不用线程!
现在我们来取消一下最大并发数,也就是注释掉这句代码
[objc] view plain copy
- q.maxConcurrentOperationCount = 2;
那么执行结果会如下:
控制台会瞬间打印出”睡1秒“,然后停顿1秒之后再几乎同时打印接下来的内容!
由此我们可以得知在没有指定最大并发数时有多少个操作添加到队列中就会开启多少条子线程,这得浪费多少资源啊!!!
使用NSOperation同样能够实现线程间通讯,如下
[objc] view plain copy
1. [self.queue addOperationWithBlock:^{
2. @"耗时的操作 %@", [NSThread currentThread]);
3.
4. // 在主线程更新 UI
5. mainQueue] addOperationWithBlock:^{
6. @"更新 UI %@", [NSThread currentThread]);
7. }];
8. }];
执行效果如下: