一、NSOperation 抽象类
- NSOperation 是一个"抽象类",不能直接使用。抽象类的用处是定义子类共有的属性和方法。
- NSOperation 是基于 GCD 做的面向对象的封装。
- 相比较 GCD 使用更加简单,并且提供了一些用 GCD 不是很好实现的功能。
- 苹果公司推荐使用的并发技术。
- 两个子类:
- NSInvocationOperation (调用)
- NSBlockOperation (块)
相比NSInvocationOperation推荐使用NSBlockOperation,代码简单,同时由于闭包性使它没有传参问题。
- NSOperationQueue 队列
已经学习过的抽象类
- UIGestureRecognizer
- CAAnimation
- CAPropertyAnimation
二、 NSOperation 和 GCD 的核心概念
- GCD的核心概念:将 任务(block) 添加到队列,并且指定执行任务的函数。
- NSOperation 的核心概念:将 操作 添加到 队列。
三、NSOperation 和 GCD的区别:
GCD
- 将任务(block)添加到队列(串行/并发/主队列),并且指定任务执行的函数(同步/异步)
- GCD是底层的C语言构成的API
- iOS 4.0 推出的,针对多核处理器的并发技术
- 在队列中执行的是由 block 构成的任务,这是一个轻量级的数据结构
- 要停止已经加入 queue 的 block 需要写复杂的代码
- 需要通过 Barrier 或者同步任务设置任务之间的依赖关系
- 只能设置队列的优先级
- 高级功能:
- 一次性 once
- 延迟操作 after
- 调度组
NSOperation
- 核心概念:把操作(异步)添加到队列。
- OC 框架,更加面向对象,是对 GCD 的封装。
- iOS 2.0 推出的,苹果推出 GCD 之后,对 NSOperation 的底层全部重写。
- Operation作为一个对象,为我们提供了更多的选择。
- 可以跨队列设置操作的依赖关系
- 可以设置队列中每一个操作的优先级
- 高级功能:
- 最大操作并发数(GCD不好做)
- 继续/暂停/全部取消
- 跨队列设置操作的依赖关系
四、代码实践
代码语言:javascript复制 1 //
2 // ViewController.m
3 // NSOperationTest
4 //
5 // Created by mayl on 2018/1/5.
6 // Copyright © 2018年. All rights reserved.
7 //
8
9 #import "ViewController.h"
10
11 @interface ViewController ()
12 @property(nonatomic, strong) NSOperationQueue *gOpQueue;
13 @end
14
15 @implementation ViewController
16
17 - (void)viewDidLoad {
18 [super viewDidLoad];
19 [self setUpUI];
20
21 // [self aysncCon];
22 [self maxConCount];
23 // [self oftenUse];
24 // [self setUpDependence];
25 // [self waitUntilFinished];
26 }
27
28 - (void)setUpUI{
29
30 //暂停,继续按钮
31 UIButton *lBtn4Pause = [UIButton buttonWithType:UIButtonTypeCustom];
32 [self.view addSubview:lBtn4Pause];
33
34 lBtn4Pause.frame = CGRectMake(10, 100, 100, 50);
35 [lBtn4Pause setTitle:@"挂起" forState:UIControlStateNormal];
36 lBtn4Pause.titleLabel.numberOfLines = 0;
37 [lBtn4Pause setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
38 [lBtn4Pause addTarget:self action:@selector(pauseBtnDidClick:) forControlEvents:UIControlEventTouchUpInside];
39
40 //取消所有任务按钮
41 UIButton *lBtn4CancelAll = [UIButton buttonWithType:UIButtonTypeCustom];
42 [self.view addSubview:lBtn4CancelAll];
43
44 lBtn4CancelAll.frame = CGRectMake(150, 100, 100, 50);
45 [lBtn4CancelAll setTitle:@"cancel all" forState:UIControlStateNormal];
46 [lBtn4CancelAll setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
47 [lBtn4CancelAll addTarget:self action:@selector(cancelAllBtnDidClick:) forControlEvents:UIControlEventTouchUpInside];
48 }
49
50 #pragma mark - action
51
52 /**
53 队列挂起,当前"没有完成的操作",是包含在队列的操作数中的。
54 队列挂起,不会影响已经执行操作的执行状态。
55 队列一旦被挂起,再添加的操作不会被调度。
56 */
57 - (void)pauseBtnDidClick:(UIButton *)btn{
58 NSLog(@"队列中操作数:%zd", self.gOpQueue.operationCount);
59 if (0 == self.gOpQueue.operationCount) {
60 NSLog(@"队列中无操作");
61 return;
62 }
63
64 NSLog(@"3:%d", self.gOpQueue.isSuspended);
65 self.gOpQueue.suspended = !self.gOpQueue.isSuspended;
66 NSLog(@"4:%d", self.gOpQueue.isSuspended);
67 if (self.gOpQueue.isSuspended) {
68 NSLog(@"队列挂起");
69 [btn setTitle:@"继续"
70 forState:UIControlStateNormal];
71 }else{
72 NSLog(@"队列继续");
73 [btn setTitle:@"挂起"
74 forState:UIControlStateNormal];
75 }
76 }
77
78 /**
79 取消队列中所有的操作。
80 不会取消正在执行中的操作。
81 不会影响队列的挂起状态
82 */
83 - (void)cancelAllBtnDidClick:(UIButton *)btn{
84 if (0 == self.gOpQueue.operationCount) {
85 NSLog(@"队列中无操作");
86 return;
87 }
88
89 NSLog(@"取消队列中所有操作,此方法不会改变队列挂起状态");
90 [self.gOpQueue cancelAllOperations];
91
92 NSLog(@"1:%d", self.gOpQueue.isSuspended);
93 self.gOpQueue.suspended = !self.gOpQueue.isSuspended;
94 NSLog(@"2:%d", self.gOpQueue.isSuspended);
95 }
96
97 /** 默认是:异步,并发 */
98 - (void)aysncCon{
99 NSOperationQueue *lQueue = [[NSOperationQueue alloc] init];
100 for (int i = 0; i < 20; i) {
101 [lQueue addOperationWithBlock:^{
102 [NSThread sleepForTimeInterval:1];
103 NSLog(@"%d,%@", i, [NSThread currentThread]);
104 }];
105 }
106 }
107
108 /** 最大并发数:The maximum number of queued operations that can execute at the same time.*/
109 - (void)maxConCount{
110 NSOperationQueue *lQueue = [[NSOperationQueue alloc] init];
111 lQueue.maxConcurrentOperationCount = 2;
112 for (int i = 0; i < 30; i) {
113 [lQueue addOperationWithBlock:^{
114 [NSThread sleepForTimeInterval:1];
115 NSLog(@"%d,%@", i, [NSThread currentThread]);
116 }];
117 }
118
119 self.gOpQueue = lQueue;
120 }
121
122 /** 常用:子线程耗时,主线程更新UI */
123 - (void)oftenUse{
124 NSOperationQueue *lQ = [[NSOperationQueue alloc] init];
125
126 [lQ addOperationWithBlock:^{
127 NSLog(@"耗时操作开始,%@", [NSThread currentThread]);
128 [NSThread sleepForTimeInterval:3];
129 NSLog(@"耗时操作结束");
130
131 [[NSOperationQueue mainQueue] addOperationWithBlock:^{
132 NSLog(@"主线程更新UI,%@",
133 [NSThread currentThread]);
134 }];
135
136 }];
137 }
138
139 /** 设置依赖 */
140 - (void)setUpDependence{
141 NSOperationQueue *lQ = [[NSOperationQueue alloc] init];
142
143 [lQ addOperationWithBlock:^{
144 [NSThread sleepForTimeInterval:1];
145 NSLog(@"do something,%@",
146 [NSThread currentThread]);
147 }];
148
149 NSBlockOperation *lOp1 = [NSBlockOperation blockOperationWithBlock:^{
150 [NSThread sleepForTimeInterval:1];
151 NSLog(@"1:登录,%@",
152 [NSThread currentThread]);
153 }];
154
155 NSBlockOperation *lOp2 = [NSBlockOperation blockOperationWithBlock:^{
156 [NSThread sleepForTimeInterval:1];
157 NSLog(@"2:购买点券,%@",
158 [NSThread currentThread]);
159 }];
160
161 NSBlockOperation *lOp3 = [NSBlockOperation blockOperationWithBlock:^{
162 [NSThread sleepForTimeInterval:1];
163 NSLog(@"3:使用点券,%@",
164 [NSThread currentThread]);
165 }];
166
167 NSBlockOperation *lOp4 = [NSBlockOperation blockOperationWithBlock:^{
168 [NSThread sleepForTimeInterval:1];
169 NSLog(@"4:返回结果,%@",
170 [NSThread currentThread]);
171 }];
172
173 [lOp2 addDependency:lOp1];
174 [lOp3 addDependency:lOp2];
175 [lOp4 addDependency:lOp3];
176
177 //下面加的话会循环依赖,导致任何操作都无法进行,程序不会崩溃。
178 // [lOp1 addDependency:lOp4];
179
180 [lQ addOperations:@[lOp4, lOp3] waitUntilFinished:NO];
181 [lQ addOperations:@[lOp2, lOp1] waitUntilFinished:NO];
182 }
183
184 /**执行效果如下:
185 2018-01-05 19:54:31.721539 0800 NSOperationTest[578:156322] come in
186 2018-01-05 19:54:33.727691 0800 NSOperationTest[578:156342] 0:do others,<NSThread: 0x1c027f740>{number = 3, name = (null)}
187 2018-01-05 19:54:34.731836 0800 NSOperationTest[578:156342] 1:登录,<NSThread: 0x1c027f740>{number = 3, name = (null)}
188 2018-01-05 19:54:35.737375 0800 NSOperationTest[578:156342] 2:购买点券,<NSThread: 0x1c027f740>{number = 3, name = (null)}
189 2018-01-05 19:54:36.742936 0800 NSOperationTest[578:156342] 3:使用点券,<NSThread: 0x1c027f740>{number = 3, name = (null)}
190 2018-01-05 19:54:37.746491 0800 NSOperationTest[578:156342] 4:show Time,<NSThread: 0x1c027f740>{number = 3, name = (null)}
191 2018-01-05 19:54:38.764408 0800 NSOperationTest[578:156341] 5:[lQ addOperations:@[lOp4, lOp3] waitUntilFinished:YES];实现了不设置依赖,且我需要最后执行,<NSThread: 0x1c04631c0>{number = 4, name = (null)}
192 */
193 - (void)waitUntilFinished{
194 NSOperationQueue *lQ = [[NSOperationQueue alloc] init];
195
196 NSLog(@"come in");
197 NSBlockOperation *lOp0 = [NSBlockOperation blockOperationWithBlock:^{
198 [NSThread sleepForTimeInterval:2];
199 NSLog(@"0:do others,%@",
200 [NSThread currentThread]);
201 }];
202
203 NSBlockOperation *lOp1 = [NSBlockOperation blockOperationWithBlock:^{
204 [NSThread sleepForTimeInterval:1];
205 NSLog(@"1:登录,%@",
206 [NSThread currentThread]);
207 }];
208
209 NSBlockOperation *lOp2 = [NSBlockOperation blockOperationWithBlock:^{
210 [NSThread sleepForTimeInterval:1];
211 NSLog(@"2:购买点券,%@",
212 [NSThread currentThread]);
213 }];
214
215 NSBlockOperation *lOp3 = [NSBlockOperation blockOperationWithBlock:^{
216 [NSThread sleepForTimeInterval:1];
217 NSLog(@"3:使用点券,%@",
218 [NSThread currentThread]);
219 }];
220
221 NSBlockOperation *lOp4 = [NSBlockOperation blockOperationWithBlock:^{
222 [NSThread sleepForTimeInterval:1];
223 NSLog(@"4:show Time,%@",
224 [NSThread currentThread]);
225 }];
226
227 NSBlockOperation *lOp5 = [NSBlockOperation blockOperationWithBlock:^{
228 [NSThread sleepForTimeInterval:1];
229
230 NSLog(@"5:[lQ addOperations:@[lOp4, lOp3] waitUntilFinished:YES];实现了不设置依赖,且我需要最后执行,%@",
231 [NSThread currentThread]);
232 }];
233
234 [lOp2 addDependency:lOp1];
235 [lOp3 addDependency:lOp2];
236 [lOp4 addDependency:lOp3];
237
238 //执行顺序跟在数组中的顺序无关
239 //waitUntilFinished:If YES, the current thread is blocked until all of the specified operations finish executing. If NO, the operations are added to the queue and control returns immediately to the caller.(If YES,当前线程会被阻塞,直到数组中所有操作执行完毕。下局代码是直到lOp5执行完毕,才会执行后续操作)
240 [lQ addOperations:@[lOp0] waitUntilFinished:YES];
241
242 [lQ addOperations:@[lOp2, lOp1] waitUntilFinished:NO];
243 [lQ addOperations:@[lOp4, lOp3] waitUntilFinished:YES];
244
245 [lQ addOperation:lOp5];
246 }
247
248 @end