- 多线程技术:NSOperation 与 NSOperationQ
- 多线程技术:NSOperation 与 NSOperationQ
- NSOperation&&NSOperationQueue
- iOS多线程:『NSOperation、NSOperationQ
- iOS多线程:『NSOperation、NSOperationQ
- iOS 多线程 NSOperation、NSOperationQ
- Objc多线程-NSOperation,NSOperationQ
- iOS多线程:『NSOperation、NSOperationQ
- iOS 多线程:NSOperation、NSOperationQ
- iOS多线程:『NSOperation、NSOperationQ
为什么要学习NSOperation和NSOperationQueue来实现多线程
- 比
GCD
功能更强大,可以cancel一个任务,可以在任务之间添加依赖。 - 主流的库常常使用这种技术完成复杂的设计,从侧面证明这种技术很有用,要看懂其他牛逼的库,也需要这个技术的支撑。
NSOperation的种类
NSOperation
是一个抽象类,系统预设了两种子类NSInvocationOperation
和NSBlockOperation
,同时我们也可以继承NSOperation
写自己的Operation
-
NSInvocationOperation
-
NSBlockOperation
-
自定义NSOperation的子类
不使用NSOperationQueue的NSOperation(使用场景非常少,苹果也不推荐)
单独使用NSOperation
而不配合NSOperationQueue
基本不能实现多线程,说基本是因为一种特殊情况下依然是可以实现多线程,但是即便如此,也使用较小,所以这个不是我们学习的重点,简单的总结一下
启动 ,不使用NSOperationQueue
,启动就是调用NSOperation
自己的方法 start
- 使用
NSInvocationOperation
, 不开新线程,顺序执行
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"start");
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
[invocationOperation start];
NSLog(@"end");
}
- (void)invocationOperation {
NSLog(@"do invocationOperation %@", [NSThread currentThread]);
}
输出结果
2017-10-29 09:01:58.902 TESTOption[15342:4271780] start
2017-10-29 09:01:58.902 TESTOption[15342:4271780] do invocationOperation <NSThread: 0x60800006c9c0>{number = 1, name = main}
2017-10-29 09:01:58.903 TESTOption[15342:4271780] end
- 使用
NSBlockOperation
的blockOperationWithBlock
,不开新线程,顺序执行
NSLog(@"start %@", [NSThread currentThread]);
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"do something 1 %@", [NSThread currentThread]);
}];
[blockOperation start];
NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"do something 2 %@", [NSThread currentThread]);
}];
[blockOperation2 start];
NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"do something 2 %@", [NSThread currentThread]);
}];
[blockOperation3 start];
NSLog(@"end %@", [NSThread currentThread]);
输出结果
2017-10-27 23:12:33.049 MLeaksFinderDemo[13830:3493348] start <NSThread: 0x6080000734c0>{number = 1, name = main}
2017-10-27 23:12:33.050 MLeaksFinderDemo[13830:3493348] do something 1 <NSThread: 0x6080000734c0>{number = 1, name = main}
2017-10-27 23:12:33.051 MLeaksFinderDemo[13830:3493348] do something 2 <NSThread: 0x6080000734c0>{number = 1, name = main}
2017-10-27 23:12:33.051 MLeaksFinderDemo[13830:3493348] do something 2 <NSThread: 0x6080000734c0>{number = 1, name = main}
2017-10-27 23:12:33.052 MLeaksFinderDemo[13830:3493348] end <NSThread: 0x6080000734c0>{number = 1, name = main}
- 使用
NSBlockOperation
的addExecutionBlock
,开新线程,并发,唯一一个
NSLog(@"start %@", [NSThread currentThread]);
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"do something 1 %@", [NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"do something 1 part2 %@", [NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"do something 1 part3 %@", [NSThread currentThread]);
}];
[blockOperation start];
输出结果
2017-10-28 04:31:42.580 MLeaksFinderDemo[14178:3678696] do something 1 part1 <NSThread: 0x60800007e580>{number = 1, name = main}
2017-10-28 04:31:42.580 MLeaksFinderDemo[14178:3678696] do something 1 part3 <NSThread: 0x60800007e580>{number = 1, name = main}
2017-10-28 04:31:42.581 MLeaksFinderDemo[14178:3678734] do something 1 part2 <NSThread: 0x60000026c540>{number = 3, name = (null)}
- 使用自定义Operation ,顺序执行
重写main
方法 实现基本操作
@implementation MyOperation
- (void)main {
NSLog(@"do something %@", [NSThread currentThread]);
}
@end
调用MyOperation
NSLog(@"start %@", [NSThread currentThread]);
MyOperation *operation1 = [[MyOperation alloc] init];
[operation1 start];
MyOperation *operation2 = [[MyOperation alloc] init];
[operation2 start];
NSLog(@"end %@", [NSThread currentThread]);
使用NSOperationQueue的NSOperation(使用场景多)
NSOperationQueue的种类
NOperationQueue
分为两种,系统预留的主队列和自定义队列,自定义队列不需要继承直接使用[[NOperationQueue alloc] init]
创建,主队列直接使用[NSOperationQueue mainQueue]
访问即可,放在[NSOperationQueue mainQueue]
的任务都会在主线程上执行。
串行和并行
[NSOperationQueue mainQueue]
是串行队列,放在上面的任务都会在主线程上执行
[[NOperationQueue alloc] init]
创建的队列默认是并行的,它有一个属性是
启动任务,使用addOpearation:
当一个nsoperation
对象加入到operationQueue
数组时,operation
的启动不再用 operation
自己决定。而是由operationQueue
决定,一旦一个operation
被加入operationQueue
时,operation将会在很短的时候内执行。
使用addOperation:
方法添加然后自动会启动任务。
MyOperation *operation = [[MyOperation alloc] init];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
NSBlockOperation和NSInvocationOperation使用实际例子
- (void)viewDidLoad {
[super viewDidLoad];
UIImageView *imageView = [[UIImageView alloc] init];
imageView.frame = CGRectMake(200, 200, 100, 100);
[self.view addSubview:imageView];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue = operationQueue;
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"use blockOperation %@", [NSThread currentThread]);
}];
NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
NSLog(@"start %@", [NSThread currentThread]);
[operationQueue addOperation:operation];
[operationQueue addOperation:invocation];
}
- (void)invocationOperation {
NSLog(@"use invocationOperation %@", [NSThread currentThread]);
}
输出结果
2017-10-29 11:58:44.278 MLeaksFinderDemo[16434:4589155] start <NSThread: 0x608000062a00>{number = 1, name = main}
2017-10-29 11:58:44.279 MLeaksFinderDemo[16434:4589195] use invocationOperation <NSThread: 0x6080000711c0>{number = 3, name = (null)}
2017-10-29 11:58:44.279 MLeaksFinderDemo[16434:4589198] use blockOperation <NSThread: 0x6080000710c0>{number = 4, name = (null)}
###自定义Operation使用main 方法
3个log信息在不同线程,证明现在确实实现了多线程。
自定义Operation
NSOperation有两个方法start
和 main
方法。它们在启动后会顺序执行。
我们可以写一个自定义的Operation的子类来验证一下。
@implementation MyOperation
- (void)start {
NSLog(@"start");
[super start];
}
- (void)main {
NSLog(@"main");
[super main];
}
@end
@implementation DetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.operationQueue = [[NSOperationQueue alloc] init];
MyOperation *operation = [[MyOperation alloc] init];
[self.operationQueue addOperation:operation];
}
@end
输出
2017-10-29 12:09:33.250 MLeaksFinderDemo[16565:4629369] start
2017-10-29 12:09:35.194 MLeaksFinderDemo[16565:4629369] main
使用自定义Operation来实现多线程,按照官网的教材,约定有两种方法。
重写main
或者start
方法中的其中一个。main
比较简单
main方法实现定制
- 继承NSOperation类
- 重写“main”方法
- 在“main”方法中创建一个“autoreleasepool”
- 将你的代码放在“autoreleasepool”中
正确响应取消事件
operation开始执行之后,会一直执行任务直到完成,或者显式地取消操作。取消可能发生在任何时候,甚至在operation执行之前。尽管NSOperation提供了一个方法,让应用取消一个操作,但是识别出取消事件则是我们自己的事情。如果operation直接终止, 可能无法回收所有已分配的内存或资源。因此operation对象需要检测取消事件,并优雅地退出执行
NSOperation对象需要定期地调用isCancelled方法检测操作是否已经被取消,如果返回YES(表示已取消),则立即退出执行。不管是自定义NSOperation子类,还是使用系统提供的两个具体子类,都需要支持取消。isCancelled方法本身非常轻量,可以频繁地调用而不产生大的性能损失
以下地方可能需要调用isCancelled
:
- 在执行任何实际的工作之前
- 在循环的每次迭代过程中,如果每个迭代相对较长可能需要调用多次
- 代码中相对比较容易中止操作的任何地方
- (void)main {
@autoreleasepool {
if (self.isCancelled) {
return;
}
for (NSInteger i = 0; i < 10000; i++) {
if (self.isCancelled) {
return;
}
NSLog(@"do something %ld", i / 100);
}
}
}
@end
总结
NSOperation本身内容比较繁杂,另外开一个文章介绍更多复杂和高级功能
网友评论