多线程-NSOperation
2018-02-06 本文已影响13人
mtry
概序
- NSOperation 可以看成任务抽象类,管理了任务的状态变化、优先级、依赖性和取消等操作。
- NSOperationQueue 就是用来管理NSOperation的队列,可以控制任务的并发数和挂起恢复等操作。
NSOperation
NSOperation作为一个在线程安全下管理状态变化、优先级、依赖性和取消等的抽象类,直接实例意义不大,一般使用是它的子类NSBlockOperation、 NSInvocationOperation 或继承(本文不考虑继承)
状态变化
一个NSOperation的开始可以通过调用start函数,如果添加到NSOperationQueue中则开始的时机是由队列决定的。NSOperation的状态变化isReady → isExecuting → isFinished,isReady如果是NO,那么任务现在处于等待当中,isExecuting(执行中)和isFinished(完成)则是互斥的
优先级
通过设置NSOperation的优先级(queuePriority)可以控制任务向线程派发的顺序
- NSOperationQueuePriorityVeryHigh
- NSOperationQueuePriorityHigh
- NSOperationQueuePriorityNormal
- NSOperationQueuePriorityLow
- NSOperationQueuePriorityVeryLow
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.suspended = YES;
for(NSInteger i = 0; i < 3; i++)
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"优先级低任务:%ld", i);
}];
operation.queuePriority = NSOperationQueuePriorityVeryLow;
[queue addOperation:operation];
}
for(NSInteger i = 0; i < 3; i++)
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"优先级中任务:%ld", i);
}];
operation.queuePriority = NSOperationQueuePriorityNormal;
[queue addOperation:operation];
}
for(NSInteger i = 0; i < 3; i++)
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"优先级高任务:%ld", i);
}];
operation.queuePriority = NSOperationQueuePriorityVeryHigh;
[queue addOperation:operation];
}
queue.suspended = NO;
结果
12:32:16.044 ObjectiveDemo[1215:69942] 优先级高任务:0
12:32:16.044 ObjectiveDemo[1215:69964] 优先级高任务:1
12:32:16.044 ObjectiveDemo[1215:69963] 优先级高任务:2
12:32:16.044 ObjectiveDemo[1215:70022] 优先级中任务:0
12:32:16.044 ObjectiveDemo[1215:69964] 优先级中任务:1
12:32:16.044 ObjectiveDemo[1215:69963] 优先级中任务:2
12:32:16.044 ObjectiveDemo[1215:69942] 优先级低任务:0
12:32:16.045 ObjectiveDemo[1215:70022] 优先级低任务:2
12:32:16.045 ObjectiveDemo[1215:69935] 优先级低任务:1
注意:只能控制队列中还没派发的任务顺序
依赖性
- 如果operation1依赖operation2,那么operation1必须在operation2完成才能开始
- 一个operation可以同时依赖多个操作,可以构成一颗多叉树,所有子节点完成之后,才开始执行父节点
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行任务:1");
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行任务:2");
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行任务:3");
}];
NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行任务:4");
}];
NSBlockOperation *operation5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行任务:5");
}];
NSBlockOperation *operation6 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行任务:6");
}];
[operation1 addDependency:operation2];
[operation1 addDependency:operation3];
[operation1 addDependency:operation4];
[operation4 addDependency:operation5];
[operation4 addDependency:operation6];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
[queue addOperation:operation5];
[queue addOperation:operation6];
结果:4一定在5和6之后执行,1一定在2、3、4之后执行
14:10:11.133 ObjectiveDemo[1421:104016] 执行任务:5
14:10:11.133 ObjectiveDemo[1421:104022] 执行任务:3
14:10:11.133 ObjectiveDemo[1421:104024] 执行任务:2
14:10:11.133 ObjectiveDemo[1421:104010] 执行任务:6
14:10:11.134 ObjectiveDemo[1421:104010] 执行任务:4
14:10:11.134 ObjectiveDemo[1421:104016] 执行任务:1
注意:如果依赖关系出现环就会死锁
取消
相比GCD的取消NSOperation真是直接太多了。不过需要注意的是只能取消还没操作队列中还没执行的任务。
NSBlockOperation
能够并发地执行一个或多个block对象,所有相关的block都执行完之后,任务才算完成
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1");
}];
[operation addExecutionBlock:^{
NSLog(@"2");
}];
[operation addExecutionBlock:^{
NSLog(@"3");
}];
[operation setCompletionBlock:^{
NSLog(@"done");
}];
[operation start];
结果
13:24:25.338 ObjectiveDemo[1317:86738] 1
13:24:25.338 ObjectiveDemo[1317:86790] 3
13:24:25.338 ObjectiveDemo[1317:86779] 2
13:24:25.340 ObjectiveDemo[1317:86779] done
NSInvocationOperation
基于一个target和SEL来创建操作,如果你已经有现有的方法来执行需要的任务,选它还是很方便的
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg
当然如果需要传多个参数可以使用
- (instancetype)initWithInvocation:(NSInvocation *)inv
NSMethodSignature *methodSignature = [self methodSignatureForSelector:@selector(callBackArg1:arg2:)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
invocation.target = self;
invocation.selector = @selector(callBackArg1:arg2:);
NSString *arg1 = @"111";
NSString *arg2 = @"222";
//设置参数的索引时不能从0开始,因为0已经被self占用,1已经被_cmd占用
[invocation setArgument:&arg1 atIndex:2];
[invocation setArgument:&arg2 atIndex:3];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithInvocation:invocation];
[operation setCompletionBlock:^{
NSLog(@"done");
}];
[operation start];
- (void)callBackArg1:(id)arg1 arg2:(id)arg2
{
NSLog(@"arg1 = %@ arg2 = %@", arg1, arg2);
}
NSOperationQueue
管理NSOperation的队列,控制任务的并发数和挂起等操作。并发数和挂起恢复操作只需简单的设置maxConcurrentOperationCount和suspended参数即可
注意
- 子线程派发任务队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]
- 主线程派发任务队列 NSOperationQueue *queue = [NSOperationQueue mainQueue]