介绍

NSOperation是苹果对GCD的一个抽象封装。GCD 是基于 C 的底层的 API ,而 NSOperation 则是 GCD 实现的 Objective-C API。 虽然 NSOperation 是基于 GCD 实现的, 但是并不意味着它是一个 GCD 的 “dumbed-down” 版本, 相反,我们可以用NSOperation 轻易的实现一些 GCD 要写大量代码的事情。可以实现GCD不能实现的中断线程,合并等操作。

NSOperation相比GCD的优势

  • 提供了在 GCD 中不那么容易复制的有用特性。
  • 可以很方便的取消一个NSOperation的执行
  • 可以更容易的添加任务的依赖关系
  • 提供了任务的状态:isReady → isExecuting → isFinished 对应 准备 → 执行 → 完成

1.使用

  1. 建立一个NSOperationQueue的对象

  2. 建立一个NSOperation的对象

  3. 将operation加入到NSOperationQueue中
    跟GCD一模一样,谁让抽象自GCD

IOS提供了两个常用的,NSInvocationOperation,NSInvocationOperation是NSOperation的子类,允许运行在operation中的targer和selector。

1.NSInvocationOperation基础例子(mainQueue是串行的.)

1
2
3
4
5
6
7
8
9
10
11
12
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [NSOperationQueue mainQueue];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"hello"];
[queue addOperation:operation];
NSLog(@"i'm back");
}

-(void)download:(id)data{
sleep(5);
NSLog(@"dat:%@, thread:%@",data,[NSThread currentThread]);
}

看下方输出可以看出先执行了“i’m back”, 五秒后执行download方法里的打印。并没有阻塞主线程。

1
2
2015-08-26 17:35:32.536 test[26697:5430809] i'm back
2015-08-26 17:35:37.585 test[26697:5430809] thread:\<NSThread: 0x7f92c0d27fe0\>{number = 1, name = main}

2.NSBlockOperation基础例子

1
2
3
4
5
6
7
8
9
10
11
(void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [NSOperationQueue mainQueue];

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
sleep(5);
NSLog(@"thread:%@",[NSThread currentThread]);
}];
[queue addOperation:blockOperation];
NSLog(@"i'm back");
}

输出结果是一样的:

1
2
2015-08-26 17:37:30.827 test[26721:5446451] i'm back
2015-08-26 17:37:35.875 test[26721:5446451] thread:\<NSThread: 0x7fc363627f60\>{number = 1, name = main}

2.进阶

1.mainQueue是串行的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [NSOperationQueue mainQueue];
// NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:nil];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:nil];
NSInvocationOperation *operation3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:nil];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
NSLog(@"i'm back");
}

-(void)download:(id)data{
sleep(2);
NSLog(@"dat:%@, thread:%@",data,[NSThread currentThread]);
}

打印结果:串行执行,两秒两秒

1
2
3
4
2015-08-26 17:45:14.381 test[26889:5494493] i'm back
2015-08-26 17:45:16.411 test[26889:5494493] dat:(null), thread:<NSThread: 0x7fdccad27f80>{number = 1, name = main}
2015-08-26 17:45:18.419 test[26889:5494493] dat:(null), thread:<NSThread: 0x7fdccad27f80>{number = 1, name = main}
2015-08-26 17:45:20.428 test[26889:5494493] dat:(null), thread:<NSThread: 0x7fdccad27f80>{number = 1, name = main}

2.自定义NSOperationQueue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)viewDidLoad {
[super viewDidLoad];
// NSOperationQueue *queue = [NSOperationQueue mainQueue];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:nil];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:nil];
NSInvocationOperation *operation3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:nil];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
NSLog(@"i'm back");
}

-(void)download:(id)data{
sleep(2);
NSLog(@"dat:%@, thread:%@",data,[NSThread currentThread]);
}

打印结果:并行执行,都是2秒后统一打印

1
2
3
4
2015-08-26 17:46:56.371 test[26927:5507100] i'm back
2015-08-26 17:46:58.373 test[26927:5507197] dat:(null), thread:<NSThread: 0x7fd679400140>{number = 2, name = (null)}
2015-08-26 17:46:58.373 test[26927:5507200] dat:(null), thread:<NSThread: 0x7fd679727dd0>{number = 4, name = (null)}
2015-08-26 17:46:58.373 test[26927:5507198] dat:(null), thread:<NSThread: 0x7fd679673ba0>{number = 3, name = (null)}

3.添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)viewDidLoad {
[super viewDidLoad];
// NSOperationQueue *queue = [NSOperationQueue mainQueue];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"i'm 1"];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"i'm 2"];
NSInvocationOperation *operation3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"i'm 3"];
[operation3 addDependency:operation1]; //依赖

[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
NSLog(@"i'm back");
}

-(void)download:(id)data{
sleep(2);
NSLog(@"dat:%@, thread:%@",data,[NSThread currentThread]);
}

打印结果:operation3 在 operation1执行完成后才能执行

1
2
3
4
2015-08-26 17:49:15.123 test[26986:5516833] i'm back
2015-08-26 17:49:17.124 test[26986:5516907] dat:i'm 2, thread:<NSThread: 0x7fbffa5b0070>{number = 3, name = (null)}
2015-08-26 17:49:17.124 test[26986:5516908] dat:i'm 1, thread:<NSThread: 0x7fbffa73f060>{number = 2, name = (null)}
2015-08-26 17:49:19.129 test[26986:5516907] dat:i'm 3, thread:<NSThread: 0x7fbffa5b0070>{number = 3, name = (null)}

4.设置最大并行数

1
[queue setMaxConcurrentOperationCount:5];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (void)viewDidLoad {
[super viewDidLoad];
// NSOperationQueue *queue = [NSOperationQueue mainQueue];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue setMaxConcurrentOperationCount:5];
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"i'm 1"];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"i'm 2"];
NSInvocationOperation *operation3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"i'm 3"];
NSInvocationOperation *operation4 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"i'm 4"];
NSInvocationOperation *operation5 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"i'm 5"];
NSInvocationOperation *operation6 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"i'm 6"];

[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
[queue addOperation:operation5];
[queue addOperation:operation6];
}

-(void)download:(id)data{
sleep(1);
NSLog(@"dat:%@, thread:%@",data,[NSThread currentThread]);
}

打印结果:operation6在其他完成了才能执行。因为限制了最大并发数为5

1
2
3
4
5
6
2015-08-26 17:54:07.075 test[27065:5546263] dat:i'm 2, thread:<NSThread: 0x7fedd8725a70>{number = 6, name = (null)}
2015-08-26 17:54:07.075 test[27065:5546265] dat:i'm 1, thread:<NSThread: 0x7fedd86a2270>{number = 4, name = (null)}
2015-08-26 17:54:07.076 test[27065:5546292] dat:i'm 4, thread:<NSThread: 0x7fedd8420520>{number = 3, name = (null)}
2015-08-26 17:54:07.076 test[27065:5546293] dat:i'm 5, thread:<NSThread: 0x7fedd841a5a0>{number = 5, name = (null)}
2015-08-26 17:54:07.075 test[27065:5546291] dat:i'm 3, thread:<NSThread: 0x7fedd8702ec0>{number = 2, name = (null)}
2015-08-26 17:54:08.081 test[27065:5546265] dat:i'm 6, thread:<NSThread: 0x7fedd86a2270>{number = 4, name = (null)}