
目录
一、基础理论
1.1 NSOperation概念
1.2 为什么要使用 NSOperation、NSOperationQueue?
1.3 万变不离其宗之术语巩固
1.4 NSOperation的使用步骤
二、NSOperation的基本操作
2.1 使用子类NSInvocationOperation
2.2 使用子类NSBlockOperation
2.3 使用NSBlockOperation 的addExecutionBlock操作
2.4 控制最大并发量
2.5 NSOperation操作依赖
2.6 NSOperation、NSOperationQueue线程间的通信
2.7 任务执行状态控制(待续......)
三、结合案例使用
案例1:子线程下载图片->主线程显示图片
案例2:有 A、B 两个任务,要等 A 执行完,B 才能执行
案例3:售票的小故事
一、基础理论
1.1 NSOperation概念
NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用,代码可读性更高。它使用起来比GCD更灵活,功能也更加强大。NSOperation需要配合NSOperationQueue来实现多线程,因为在默认情况下,NSOperation单独使用时系统同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行。
NSOperation是个抽象类,并不能封装任务,我们只有使用它的子类(NSInvocationOperation和NSBlockOperation)来封装任务。
1.2 为什么要使用 NSOperation、NSOperationQueue?
- 可添加完成的代码块,在操作完成后执行。
- 添加操作之间的依赖关系,方便的控制执行顺序。
- 支持设置执行的优先级,从而影响 operation 之间的相对执行顺序。
- 支持取消操作,可以允许我们停止正在执行的 operation 。
- 使用 KVO 观察对操作执行状态的更改:isExecuteing( 代表任务正在执行中)、isFinished( 代表任务已经执行完成)、isCancelled( 代表任务已经取消执行)。
1.3 万变不离其宗之术语巩固
串行 vs 并发
区别:在于允许同时执行的任务数量
串行: 一次只能执行一个任务,必须等上一个任务执行完成之后才能执行下一个任务
并发:允许多个任务同时执行
同步 vs 异步
区别::在于能否开启新的线程
同步: 在当前线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力
队列 vs 线程
队列: 指的是串行队列和并发队列
线程: 是否具有开启线程的能力
Operation vs GCD
Operation:相对于GCD来说,更加强大。可以给operation之间添加依赖关系、取消一个正在执行的operation、暂停和恢复operationQueue等
GCD: 是一种更轻量级的,以FIFO(先进先出,后进后出)的顺序执行并发任务。使用GCD我们并不用关心任务的调度情况,而是系统会自动帮我们处理。但是GCD的短板也是非常明显的,比如我们想要给任务之间添加依赖关系、取消或者暂停一个正在执行的任务时就会变得束手无策。
1.4 NSOperation的使用步骤
- 创建操作:先将需要执行的操作封装到一个NSOperation对象中
- 创建队列:创建NSOperationQueue对象
- 将操作加入队列中: 将NSOperation对象添加到NSOperationQueue对象中
综上之后
系统就会自动将NSOperationQueue中的NSOperation取出来,在新线程中执行操作
二、NSOperation的基本操作
NSOperation是个抽象类,不能用来封装操作。我们只有使用它的子类来封装操作。
- 使用子类NSInvocationOperation
- 使用子类NSBlockOperation
- 自定义继承自NSOperation的子类, 通过实现内部相应的方法来封装操作
在不使用NSOperationQueue,单独使用NSOperation的情况下系统同步执行操作
2.1 使用子类NSInvocationOperation
- NSInvocationOperation: 操作是在当前线程执行的,并没有开启新线程。
- (void)userInvocationOperation{
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(texttest) object:nil];
//调用 start 方法开始执行操作
[operation start];
}
- (void)texttest{
NSLog(@"%@",[NSThread currentThread]);
//打印结果为: <NSThread: 0x60800007a740>{number = 1, name = main}
}
2.2 使用子类NSBlockOperation
- NSBlockOperation:操作是在当前线程执行的,并没有开启新线程
- (void)userBolockOperation{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@",[NSThread currentThread]);
//打印结果为: <NSThread: 0x60800006b540>{number = 1, name = main}
}];
[operation start];
}
2.3 使用NSBlockOperation 的addExecutionBlock操作
-
blockOperationWithBlock:操作是在主线程执行的,addExecutionBlock方法中的操作是在其他线程中执行的。但当添加的操作的数量较多时,就会自动开启新线程。
blockOperationWithBlock:
中的操作也可能会在其他线程中执行
- (void)addExecutionBlock{
NSBlockOperation *p = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1------%@", [NSThread currentThread]);
}];
// 添加额外的任务
[p addExecutionBlock:^{
NSLog(@"2------%@", [NSThread currentThread]);
for (int i = 0 ; i < 10000; i++) {
if (i == 9999){
NSLog(@"耗时操作");
}
}
}];
[p addExecutionBlock:^{
NSLog(@"3------%@", [NSThread currentThread]);
}];
[p addExecutionBlock:^{
NSLog(@"4------%@", [NSThread currentThread]);
}];
[p start];
}

从打印结果我们可以看出,blockOperationWithBlock:
方法中的操作是在主线程中执行的,
而addExecutionBlock:方法中的操作是在其他线程中执行的。
但是请继续往下看
*前方高能
- (void)addExecutionBlock{
NSBlockOperation *p = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1------%@", [NSThread currentThread]);
}];
// 添加额外的任务
[p addExecutionBlock:^{
NSLog(@"2------%@", [NSThread currentThread]);
for (int i = 0 ; i < 10000; i++) {
if (i == 9999){
NSLog(@"耗时操作");
}
}
}];
[p addExecutionBlock:^{
NSLog(@"3------%@", [NSThread currentThread]);
}];
[p addExecutionBlock:^{
NSLog(@"4------%@", [NSThread currentThread]);
}];
[p addExecutionBlock:^{
NSLog(@"5------%@", [NSThread currentThread]);
}];
[p start];
}

小结
如果添加的操作多的情况下,blockOperationWithBlock:
中的操作也可能会在其他线程中执行,这是由系统决定的,并不是说添加到blockOperationWithBlock:
中的操作一定是在当前线程中执行。NSBlockOperation
是否开启新线程,取决于操作的数量,如果添加的操作数量多的话,就会自动开启新线程。当然开启的线程数是由系统来决定的。
2.4 控制最大并发量
使用
NSOperationQueue
中的maxConcurrentOperationCount
的属性来控制串行执行、并发执行
最大并发操作数(maxConcurrentOperationCount
): 用来控制一个特定队列中可以有多少个操作同时参与并发执行。 控制的不是并发线程的数量,而是一个队列中同时能并发执行的最大操作数。而且一个操作也并非只能在一个线程中运行。
-
maxConcurrentOperationCount
默认为-1,表示不进行限制,可进行并发执行。 -
maxConcurrentOperationCount
为1时,队列为串行队列。只能串行执行。 -
maxConcurrentOperationCount
大于1时,队列为并发队列。操作并发执行。
- (void)setupMaxConcurrentOperationCount{
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//queue.maxConcurrentOperationCount = 1;
queue.maxConcurrentOperationCount = 2;
[queue addOperationWithBlock:^{
NSLog(@"1------%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"2------%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"3------%@", [NSThread currentThread]);
}];
}


2.5 NSOperation操作依赖
- (void)addDependency{
//1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.创建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行任务A----%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"执行任务B----%@",[NSThread currentThread]);
}];
//3.添加依赖
[op2 addDependency:op1]; // 让op2 依赖于 op1,则先执行op1,在执行op2
//4.添加操作到队列中
[queue addOperation:op1];
[queue addOperation:op2];
}
2.6 NSOperation、NSOperationQueue线程间的通信
- (void)mainRefreshUI{
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 2.添加操作
[queue addOperationWithBlock:^{
// 异步模拟执行耗时操作
for (int i = 0; i < 100; i++) {
NSLog(@"1---%@", [NSThread currentThread]);
}
// 回到主线程刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"2---%@", [NSThread currentThread]);
}];
}];
}

2.7 任务执行状态控制
暂未吃透
~~待续
三、结合案例使用
案例1:子线程下载图片->主线程显示图片
//1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.添加操作
[queue addOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"https://img.haomeiwen.com/i1658521/929b88123cf7156c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
self.imgView.image = image;
}];
}];
案例2:有 A、B 两个任务,要等 A 执行完,B 才能执行
// 添加依赖 控制执行顺序
//1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.创建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 1000; i++) {
if (i == 999) {
NSLog(@"----模拟添加耗时操作1111----");
}
}
NSLog(@"执行任务A----%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 10; i++) {
if (i == 9) {
NSLog(@"----模拟添加耗时操作2222----");
}
}
NSLog(@"执行任务B----%@",[NSThread currentThread]);
}];
//3.添加依赖
[op2 addDependency:op1]; // 让op2 依赖于 op1,则先执行op1,在执行op2
//4.添加操作到队列中
[queue addOperation:op1];
[queue addOperation:op2];

案例3:售票的小故事
//声明两个属性
@property(nonatomic,assign)NSInteger ticketSurplusCount; //总票数
@property(nonatomic,strong)NSLock *lock; //用来处理线程安全
//售票的小故事
- (void)buyTicketSafe{
_ticketSurplusCount = 30;
self.lock = [[NSLock alloc]init];
NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
queue1.maxConcurrentOperationCount = 1;
NSOperationQueue *queue2 = [[NSOperationQueue alloc]init];
queue2.maxConcurrentOperationCount = 1;
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[self saleTicketSafa];
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[self saleTicketSafa];
}];
//添加操作
[queue1 addOperation:op1];
[queue2 addOperation:op2];
}
- (void)saleTicketSafa{
while (1) {
//加锁
[self.lock lock];
if (self.ticketSurplusCount > 0) {
self.ticketSurplusCount --;
NSLog(@"剩余票数:%zd 窗口:%@",self.ticketSurplusCount,[NSThread currentThread]);
}
//解锁
[self.lock unlock];
if (self.ticketSurplusCount <=0) {
NSLog(@"不好意思。。票买完了。。。。");
break;
}
}
}

网友评论