iOS多线程之NSOperation

NSOperation简介

NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性也更高。

1. NSOperation及其子类

NSOperation是个抽象类,并不能封装任务。我们只有使用它的子类来封装任务。

  • NSInvocationOperation
  • NSBlockOperation
  • 自定义NSOperation
1.1 NSInvocationOperation
- (void)invocationOperationTest {
    NSLog(@"InvocationOperationCurrentThread:%@", [NSThread currentThread]); // 打印当前线程
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(taskTest:) object:@{@"name":@"InvocationOperation"}];
    [operation start];
}

- (void)taskTest:(NSDictionary *)dict {
    NSString *name = [dict valueForKey:@"name"];
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"%@---%@", name,[NSThread currentThread]); // 打印当前线程
    }
}
//输出结果
2018-05-24 17:14:46.893 MultiThreadingDemo[3979:594628] InvocationOperationCurrentThread:{number = 1, name = main}
2018-05-24 17:14:48.895 MultiThreadingDemo[3979:594628] InvocationOperation---{number = 1, name = main}
2018-05-24 17:14:50.897 MultiThreadingDemo[3979:594628] InvocationOperation---{number = 1, name = main}

从结果来看,NSInvocationOperation是在主线程中执行的,并没有开辟新线程,那是不是说明NSInvocationOperation一定在主线程执行呢?
show the code测试一下

- (void)invocationInOtherThread {
    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(invocationOperationTest) object:nil];
    thread1.name=@"invocationThread";
    [thread1 start];
}
//输出结果
2018-05-24 17:23:33.505 MultiThreadingDemo[3979:608510] InvocationOperationCurrentThread:{number = 3, name = invocationThread}
2018-05-24 17:23:35.511 MultiThreadingDemo[3979:608510] InvocationOperation---{number = 3, name = invocationThread}
2018-05-24 17:23:37.514 MultiThreadingDemo[3979:608510] InvocationOperation---{number = 3, name = invocationThread}

结果证明我们之前的推测是错误的,NSInvocationOperation在我们新开辟的线程中执行。

结论:单独使用子类 NSInvocationOperation 执行一个操作的情况下,操作是在当前线程(非主线程)执行的,并没有开启新线程。

1.2 NSBlockOperation

首先看下API提供的方法:

//类方法
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
//实例方法
- (void)addExecutionBlock:(void (^)(void))block;
//封装的任务
@property (readonly, copy) NSArray *executionBlocks;

上代码

- (void)blockOperationTest {
    NSLog(@"BlockOperationCurrentThread:%@", [NSThread currentThread]); // 打印当前线程
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        [self taskTest:@{@"name":@"BlockOperation1"}];
    }];
    [operation addExecutionBlock:^{
        [self taskTest:@{@"name":@"BlockOperation2"}];
    }];
    [operation addExecutionBlock:^{
        [self taskTest:@{@"name":@"BlockOperation3"}];
    }];
    [operation start];
}
//输出结果
2018-05-24 17:29:15.550 MultiThreadingDemo[4000:617102] BlockOperationCurrentThread:{number = 1, name = main}
2018-05-24 17:29:17.553 MultiThreadingDemo[4000:617102] BlockOperation1---{number = 1, name = main}
2018-05-24 17:29:17.580 MultiThreadingDemo[4000:617158] BlockOperation2---{number = 3, name = (null)}
2018-05-24 17:29:17.580 MultiThreadingDemo[4000:617157] BlockOperation3---{number = 4, name = (null)}
2018-05-24 17:29:19.554 MultiThreadingDemo[4000:617102] BlockOperation1---{number = 1, name = main}
2018-05-24 17:29:19.655 MultiThreadingDemo[4000:617158] BlockOperation2---{number = 3, name = (null)}
2018-05-24 17:29:19.655 MultiThreadingDemo[4000:617157] BlockOperation3---{number = 4, name = (null)}

结论:和NSInvocationOperation相比,NSBlockOperation对象不用添加到操作队列也能开启新线程,但是开启新线程是有条件的。前提是一个blockOperation中需要封装多个任务。如果只开启一个任务,默认会在当前线程执行。

NSOperation是基于GCD更高一层的封装,同样拥有任务的概念,那么我们可不可以简单的认为NSInvocationOperation类似于dispatch_sync,NSBlockOperation类似于dispatch_async呢?个人意见,欢迎指正。

1.3 自定义NSOperation子类

稍后更新...

2. NSOperationQueue

队列,回想GCD里面的dispatch_queue_t中...
切入正题:

NSOperation 需要配合 NSOperationQueue 来实现多线程。因为默认情况下,NSOperation 单独使用时系统同步执行操作,配合 NSOperationQueue 我们能更好的实现异步执行。(copy自https://www.jianshu.com/p/4b1d77054b35)

2.1 NSOperationQueue创建

NSOperation 实现多线程的使用步骤分为三步:

  1. 创建操作:先将需要执行的操作封装到一个 NSOperation 对象中。
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(taskTest:) object:@{@"name":@"Invocation1InQueue"}];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(taskTest:) object:@{@"name":@"Invocation2InQueue"}];
  1. 创建队列:创建 NSOperationQueue 对象。
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  1. 将操作加入到队列中:将 NSOperation 对象添加到 NSOperationQueue 对象中。
[queue addOperation:operation1];
[queue addOperation:operation2];
2.2 NSOperationQueue中的依赖

NSOperation、NSOperationQueue 最吸引人的地方是它能添加操作之间的依赖关系。通过操作依赖,我们可以很方便的控制操作之间的执行先后顺序。

看下api中关于dependcy的几个方法
addDependency:添加依赖
removeDependency:移除依赖
NSArray *dependencies在当前操作开始执行之前完成执行的所有操作对象数组。比如A依赖B,B依赖C,那么A的dependencies就是BC

- (void)dependencyQueueTest {
    //依赖在开发中还是经常使用到的:有 A、B 两个操作,其中 A 执行完操作,B 才能执行操作
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //添加InvocationOperation
    NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(taskTest:) object:@{@"name":@"dependency1InQueue"}];
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(taskTest:) object:@{@"name":@"dependency2InQueue"}];
    //添加BlockOperation
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        [self taskTest:@{@"name":@"dependency3InQueue"}];
    }];
    [operation1 addDependency:operation3];//operation1依赖operation3
    [operation2 addDependency:operation1];//operation2依赖operation1
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
}
//输出结果:
2018-05-24 18:11:48.881 MultiThreadingDemo[4000:656437] dependency3InQueue---{number = 5, name = (null)}
2018-05-24 18:11:50.945 MultiThreadingDemo[4000:656437] dependency3InQueue---{number = 5, name = (null)}
2018-05-24 18:11:53.012 MultiThreadingDemo[4000:656437] dependency1InQueue---{number = 5, name = (null)}
2018-05-24 18:11:55.085 MultiThreadingDemo[4000:656437] dependency1InQueue---{number = 5, name = (null)}
2018-05-24 18:11:57.114 MultiThreadingDemo[4000:680839] dependency2InQueue---{number = 15, name = (null)}
2018-05-24 18:11:59.147 MultiThreadingDemo[4000:680839] dependency2InQueue---{number = 15, name = (null)}

优先级NSOperationQueuePriority

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,//最低
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,//默认
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8//最高
};
2.3 优先级queuePriority

queuePriority是NSOperation的属性,并不是Queue的,是operation在queue中的优先级

- (void)queuePriorityTest {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //添加operation1,设置operation1的优先级为VeryLow
    NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(taskTest:) object:@{@"name":@"PriorityVeryLow"}];
    operation1.queuePriority = NSOperationQueuePriorityVeryLow;
    //添加operation2,默认优先级为normal
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(taskTest:) object:@{@"name":@"PriorityNormal"}];
    //添加operation3,优先级为VeryHigh
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        [self taskTest:@{@"name":@"PriorityVeryHigh"}];
    }];
    operation3.queuePriority = NSOperationQueuePriorityVeryHigh;
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
}
//输出结果
2018-05-24 18:17:55.987 MultiThreadingDemo[4000:656437] PriorityVeryLow---{number = 5, name = (null)}
2018-05-24 18:17:55.987 MultiThreadingDemo[4000:690639] PriorityNormal---{number = 16, name = (null)}
2018-05-24 18:17:55.987 MultiThreadingDemo[4000:690641] PriorityVeryHigh---{number = 17, name = (null)}
2018-05-24 18:17:57.996 MultiThreadingDemo[4000:656437] PriorityVeryLow---{number = 5, name = (null)}
2018-05-24 18:17:57.996 MultiThreadingDemo[4000:690639] PriorityNormal---{number = 16, name = (null)}
2018-05-24 18:17:57.996 MultiThreadingDemo[4000:690641] PriorityVeryHigh---{number = 17, name = (null)}

从结果看:queuePriority跟依赖是不一样的,并不是优先级高的先执行完再执行优先级低的。

2.4 最大并发操作数:maxConcurrentOperationCount

maxConcurrentOperationCount 默认情况下为-1,表示不进行限制,可进行并发执行。
maxConcurrentOperationCount 为1时,队列为串行队列。只能串行执行。
maxConcurrentOperationCount 大于1时,队列为并发队列。

这个比较简单,就先不上code了
可以对比一下GCD的信号量semaphore

2.5 线程之间通信

最常用的:异步操作完成之后回主线程更新UI

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:0.5]; // 模拟耗时操作
    // 执行完当前任务刷新UI
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        NSLog(@"task1 have done");
    }];
}];
2.6 线程锁

未完待续...

参考链接:(排名不分先后)
https://www.jianshu.com/p/4b1d77054b35
https://www.jianshu.com/p/e6cd25f6da91
https://www.jianshu.com/p/6861d36b1c8e

你可能感兴趣的:(iOS多线程之NSOperation)