简介:NSOperation、NSOperationQueue是苹果提供给我们的一套多线程解决方案,基于 GCD 更高一层的封装,完全面向对象。
优点:
-
添加操作之间的依赖关系,方便的控制执行顺序。
-
设定操作执行的优先级。
-
可以很方便的取消,暂停一个操作的执行。
-
使用KVO 观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled
-
在操作完成后可通过completeBlock 添加代码。
一、NSOperation、NSOperationQueue 操作和操作队列
1.操作(Operation):就是你在线程中执行的那段代码,在GCD中是放在block 中的。在 NSOperation中,我们使用NSOperation 子类 NSInvocationOperation、NSBlockOperation,或者自定义子类来封装操作。
2.操作队列(Operation Queues):即用来存放操作的队列。不同于GCD 中队列先进先出的原则。NSOperationQueue 对于添加到队列中的操作,通过操作的优先级和依赖关系决定是否进入准备就绪状态,进入准备就绪的操作按顺序执行,但是执行结束的顺序不一定如此,GCD 中的并发和串行方式在这里是通过最大并发操作数(maxConcurrentOperationCount)来控制。NSOperationQueue在这里提供了两种队列:主队列和自定义队列,主队列运行在主线程上,自定义队列由系统自动分配线程
二 NSOperation、NSOperationQueue使用
NSOperation :NSOperation 是个抽象类,不能用来封装操作。我们只有使用它的子类来封装操作。NSInvocationOperation、NSBlockOperation、自定义继承自NSOperation 的子类在,不使用NSOperationQueue,单独使用NSOperation 的情况下系统同步执行操作
- NSInvocationOperation
//1.创建NSInvocationOperation对象
NSInvocationOperation*op = [[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(task1) object:nil];
// 2.调用start 方法开始执行操作
[op start];
2、NSBlockOperation
// 1.创建NSBlockOperation 对象NSBlockOperation*op = [NSBlockOperationblockOperationWithBlock:^{
for(inti = 0; i < 2; i++) {
[NSThreadsleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@", [NSThreadcurrentThread]); // 打印当前线程}
}];
// 2.调用start 方法开始执行操作
[op start];
在NSBlockOperation中我们还可以利用addExecutionBlock:后续为这个对象添加操作,值得注意的是,这个方法添加的操作可以在其他线程中完成,并且有可能导致blockOperationWithBlock:中的操作也在其他线程中完成
NSBlockOperation*op = [NSBlockOperation blockOperationWithBlock:^{
for(inti = 0; i < 2; i++) {
[NSThreadsleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@", [NSThreadcurrentThread]); // 打印当前线程} }];
// 2.添加额外的操作
for (int j = 1; j < 6; j++) {
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"%d---%@", j,[NSThread currentThread]); // 打印当前线程
}
}];
}
image.png
由结果可以看出,当额外添加任务较多时会并发执行,并且会导致最开始加入的任务随机分配线程,NSBlockOperation是否开启新线程,取决于操作的个数。如果添加的操作的个数多,就会自动开启新线程。当然开启的线程数是由系统来决定的。
3、自定义NSOperation()
4、NSOperationQueue
NSOperationQueue 一共有两种队列:主队列、自定义队列。其中自定义队列同时包含了串行、并发功能。
主队列:凡是添加到主队列中的操作,都会放到主线程中执行,需要注意的是使用addExecutionBlock:添加的额外操作,上面已经有证明。
// 主队列获取方法
NSOperationQueue *queue = [NSOperationQueue mainQueue];
自定义队列:添加到这种队列会自动放到子线程中执行,同时包含了:串行、并发功能。
// 自定义队列创建方法
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
5、NSOperation、NSOperationQueue配合使用实现多线程
5.1 将操作加入到队列中
- (void)addOpreation{=
NSOperationQueue * queue = [[NSOperationQueuealloc]init];
NSInvocationOperation* invOp = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(task)object:nil];
NSBlockOperation* blockOp = [NSBlockOperationblockOperationWithBlock:^{
[NSThreadsleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"block---%@", [NSThreadcurrentThread]);
}];
[queue addOperationWithBlock:^{
[NSThreadsleepForTimeInterval:2];// 模拟耗时操作
NSLog(@"queueAdd---%@", [NSThreadcurrentThread]);
}];
[queue addOperation:invOp];
[queue addOperation:blockOp]
}
image.png
当操作加入到队列中后就不需要手动调用start 了,且queue 会开辟新的线程并发执行
5.2 控制queue 并发数
NSOperationQueue通过最大并发数(maxConcurrentOperationCount
)来控制队列中可以有多少个操作同时参与并发执行,但是这个不是控制线程数,而是一个队列中能并发的最大操作数,
- (void)maxConcurrentOperationCount{
NSOperationQueue * queue = [NSOperationQueue mainQueue];
queue.maxConcurrentOperationCount = 1;
// queue.maxConcurrentOperationCount = 3;
for (int i=0; i<6; i++) {
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2]; //模拟耗时操作
NSLog(@"%d---%@", i,[NSThread currentThread]);
}];
}
}
queue.maxConcurrentOperationCount = 1;
image.pngqueue.maxConcurrentOperationCount = 3;
image.png由两种结果来看,maxConcurrentOperationCount只能控制每次执行操作的数量,无法控制系统开启的线程数量,线程的管理就交给系统处理
三操作的依赖
在GCD 中我们要实现一个任务或者一个队列要在另一个任务或队列完成后来执行,我们可以通过barrier,group中的一些方法,而在NSOperationQueue 中则只需要使用依赖就可以实现NSOperation提供了3个接口供我们管理和查看依赖。
addDependency:(添加) 、removeDependency:(移除依赖)、dependencies(只读,查看当前所有依赖)
-(void)addDependency {
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.创建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; //模拟耗时操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; //模拟耗时操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
}];
NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; //模拟耗时操作\
NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
}
}];
// 3.添加依赖
[op1 addDependency:op2]; // 让op1依赖于op2,则先执行op2,在执行op1
[op2 addDependency:op3]
// 4.添加操作到队列中
[queue addOperation:op1];
[queue addOperation:op2];
[queue 2 addOperation:op3];
}
image.png
从结果来看是先执行的操作2再执行的操作1,就算不是同一个queue 也可以根据依赖关系顺序执行
四 操作的优先级
NSOperation 提供了queuePriority(优先级)属性,它只适用于同一操作队列中的操作,默认情况下,所有新创建的操作对象优先级都是NSOperationQueuePriorityNormal。我们可以通过setQueuePriority:方法来改变当前操作在同一队列中的执行优先级
// 优先级的取值
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
当一个操作的所有依赖都已经完成时,操作对象通常会进入准备就绪状态,等待执行,优先级这个属性就是来决定这些操作的执行顺序
总结:操作执行的顺序先看依赖的操作是否完成,再看优先级的高低
网友评论