NSOperation、NSOperationQueue 是苹果提供给咱们的一套多线程解决方案。实际上NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,彻底面向对象。可是比 GCD 更简单易用、代码可读性也更高bash
为何要使用 NSOperation、NSOperationQueue?多线程
既然是基于 GCD 的更高一层的封装。那么,GCD 中的一些概念一样适用于 NSOperation、NSOperationQueue。在 NSOperation、NSOperationQueue 中也有相似的任务(操做)和队列(操做队列)的概念。并发
NSOperation 是个抽象类,不能用来封装操做。咱们只有使用它的子类来封装操做。咱们有三种方式来封装操做。框架
/**
NSInvocationOperation : 建立操做 ---> 建立队列 ---> 操做加入队列
*/
- (void)demo{
//1:建立操做
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"123"];
//2:建立队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//3:操做加入队列 --- 操做会在新的线程中
[queue addOperation:op];
}
- (void)handleInvocation:(id)op{
NSLog(@"%@ --- %@",op,[NSThread currentThread]);
}
复制代码
打印:异步
2020-01-29 15:43:11.012492+0800 001---NSOperation初体验[1628:78932] 123 --- <NSThread: 0x600001ea9080>{number = 3, name = (null)}
复制代码
NSInvocationOperation也能够手动调起,这样不会开启线程,会在当前队列执行任务ui
/**
手动调起操做
*/
- (void)demo1{
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"123"];
// 注意 : 若是该任务已经添加到队列,你再手动调回出错
// NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// [queue addOperation:op];
// 手动调起操做,默认在当前线程
[op start];
}
- (void)handleInvocation:(id)op{
NSLog(@"%@ --- %@",op,[NSThread currentThread]);
}
复制代码
打印:spa
2020-01-29 15:52:55.404726+0800 001---NSOperation初体验[1740:83188] 123 --- <NSThread: 0x60000024e940>{number = 1, name = main}
复制代码
注意:咱们尽可能不要用这种方式去执行任务,若是该任务已经添加到队列,再手动调起任务程序会崩溃线程
/**
blockOperation 初体验
*/
- (void)demo2{
//1:建立blockOperation
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@ -- %@",[NSThread mainThread],[NSThread currentThread]);
}];
//1.1 设置监听
bo.completionBlock = ^{
NSLog(@"完成了!!!");
};
//2:建立队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//3:添加到队列
[queue addOperation:bo];
}
复制代码
打印:3d
2020-01-29 15:57:30.760964+0800 001---NSOperation初体验[1812:85696] <NSThread: 0x600001a66940>{number = 1, name = (null)} -- <NSThread: 0x600001a2dec0>{number = 3, name = (null)}
2020-01-29 15:57:30.761098+0800 001---NSOperation初体验[1812:85697] 完成了!!!
复制代码
NSBlockOperation也可使用start方法手动执行,这样不会开启线程,会在当前队列执行任务。若是该任务已经添加到队列中,手动调起任务程序会崩溃code
若是使用子类 NSInvocationOperation、NSBlockOperation 不能知足平常需求,咱们可使用自定义继承自 NSOperation 的子类。
先定义一个继承 NSOperation 的子类,重写main方法。
// NHOperation.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NHOperation : NSOperation
@end
NS_ASSUME_NONNULL_END
// NHOperation.m
#import "NHOperation.h"
@implementation NHOperation
- (void)main{
if (!self.isCancelled) {
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
}
}
@end
复制代码
导入头文件NHOperation.h
- (void)demo3{
NHOperation *nhQueue = [[NHOperation alloc]init];
[nhQueue start];
}
复制代码
打印:
2020-01-29 16:20:57.036713+0800 001---NSOperation初体验[2060:94891] 1---<NSThread: 0x600001c16940>{number = 1, name = main}
2020-01-29 16:20:59.037879+0800 001---NSOperation初体验[2060:94891] 1---<NSThread: 0x600001c16940>{number = 1, name = main}
复制代码
这种自定义继承NSOperation的写法能够本身掌握线程的生命周期,SDWebImage框架重写了start,main等一系列方法,去实现下载图片模块
/**
可执行代码块
*/
- (void)demo5{
//建立操做
NSBlockOperation *ob = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
//添加执行代码块
[ob addExecutionBlock:^{
NSLog(@"这是一个执行代码块 - %@",[NSThread currentThread]);
}];
// 执行代码块 在新的线程 建立的操做在当前线程
//[ob start];
//利用队列,两个都在新的线程中
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:ob];
}
复制代码
打印:
2020-01-29 16:37:49.565971+0800 001---NSOperation初体验[2207:101558] <NSThread: 0x600002457d40>{number = 3, name = (null)}
2020-01-29 16:37:49.565991+0800 001---NSOperation初体验[2207:101555] 这是一个执行代码块 - <NSThread: 0x600002458480>{number = 4, name = (null)}
复制代码
/**
线程通信
*/
- (void)demo6{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"Noah";
[queue addOperationWithBlock:^{
NSLog(@"%@ = %@",[NSOperationQueue currentQueue],[NSThread currentThread]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"%@ --%@",[NSOperationQueue currentQueue],[NSThread currentThread]);
}];
}];
}
复制代码
打印:
2020-01-29 16:38:41.372075+0800 001---NSOperation初体验[2239:102618] <NSOperationQueue: 0x6000015ee560>{name = 'Noah'} = <NSThread: 0x6000000f9880>{number = 3, name = (null)}
2020-01-29 16:38:41.378217+0800 001---NSOperation初体验[2239:102581] <NSOperationQueue: 0x6000015ea180>{name = 'NSOperationQueue Main Queue'} --<NSThread: 0x600000086940>{number = 1, name = main}
复制代码
/**
优先级,只会让CPU有更高的概率调用,不是说设置高就必定所有先完成
*/
- (void)demo4{
NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 10; i++) {
NSLog(@"第1个操做 %d --- %@", i, [NSThread currentThread]);
}
}];
// 设置优先级 - 最高
bo1.qualityOfService = NSQualityOfServiceUserInteractive;
//建立第二个操做
NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 10; i++) {
NSLog(@"第二个操做 %d --- %@", i, [NSThread currentThread]);
}
}];
// 设置优先级 - 最低
bo2.qualityOfService = NSQualityOfServiceBackground;
//2:建立队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//3:添加到队列
[queue addOperation:bo1];
[queue addOperation:bo2];
}
复制代码
打印:
2020-01-29 16:42:21.830886+0800 001---NSOperation初体验[2296:104794] 第1个操做 0 --- <NSThread: 0x6000003f7200>{number = 3, name = (null)}
2020-01-29 16:42:21.830901+0800 001---NSOperation初体验[2296:104795] 第2个操做 0 --- <NSThread: 0x600000384a80>{number = 4, name = (null)}
2020-01-29 16:42:21.831015+0800 001---NSOperation初体验[2296:104794] 第1个操做 1 --- <NSThread: 0x6000003f7200>{number = 3, name = (null)}
2020-01-29 16:42:21.831076+0800 001---NSOperation初体验[2296:104794] 第1个操做 2 --- <NSThread: 0x6000003f7200>{number = 3, name = (null)}
2020-01-29 16:42:21.831107+0800 001---NSOperation初体验[2296:104795] 第2个操做 1 --- <NSThread: 0x600000384a80>{number = 4, name = (null)}
2020-01-29 16:42:21.831135+0800 001---NSOperation初体验[2296:104794] 第1个操做 3 --- <NSThread: 0x6000003f7200>{number = 3, name = (null)}
2020-01-29 16:42:21.831217+0800 001---NSOperation初体验[2296:104794] 第1个操做 4 --- <NSThread: 0x6000003f7200>{number = 3, name = (null)}
2020-01-29 16:42:21.831241+0800 001---NSOperation初体验[2296:104795] 第2个操做 2 --- <NSThread: 0x600000384a80>{number = 4, name = (null)}
2020-01-29 16:42:21.831306+0800 001---NSOperation初体验[2296:104794] 第1个操做 5 --- <NSThread: 0x6000003f7200>{number = 3, name = (null)}
2020-01-29 16:42:21.831351+0800 001---NSOperation初体验[2296:104795] 第2个操做 3 --- <NSThread: 0x600000384a80>{number = 4, name = (null)}
2020-01-29 16:42:21.831387+0800 001---NSOperation初体验[2296:104794] 第1个操做 6 --- <NSThread: 0x6000003f7200>{number = 3, name = (null)}
2020-01-29 16:42:21.831438+0800 001---NSOperation初体验[2296:104795] 第2个操做 4 --- <NSThread: 0x600000384a80>{number = 4, name = (null)}
2020-01-29 16:42:21.831595+0800 001---NSOperation初体验[2296:104794] 第1个操做 7 --- <NSThread: 0x6000003f7200>{number = 3, name = (null)}
2020-01-29 16:42:21.831771+0800 001---NSOperation初体验[2296:104794] 第1个操做 8 --- <NSThread: 0x6000003f7200>{number = 3, name = (null)}
2020-01-29 16:42:21.831957+0800 001---NSOperation初体验[2296:104795] 第2个操做 5 --- <NSThread: 0x600000384a80>{number = 4, name = (null)}
2020-01-29 16:42:21.834469+0800 001---NSOperation初体验[2296:104794] 第1个操做 9 --- <NSThread: 0x6000003f7200>{number = 3, name = (null)}
2020-01-29 16:42:21.834487+0800 001---NSOperation初体验[2296:104795] 第2个操做 6 --- <NSThread: 0x600000384a80>{number = 4, name = (null)}
2020-01-29 16:42:21.834579+0800 001---NSOperation初体验[2296:104795] 第2个操做 7 --- <NSThread: 0x600000384a80>{number = 4, name = (null)}
2020-01-29 16:42:21.834659+0800 001---NSOperation初体验[2296:104795] 第2个操做 8 --- <NSThread: 0x600000384a80>{number = 4, name = (null)}
2020-01-29 16:42:21.834761+0800 001---NSOperation初体验[2296:104795] 第2个操做 9 --- <NSThread: 0x600000384a80>{number = 4, name = (null)}
复制代码
- (void)demo7{
NSOperationQueue *queue = [NSOperationQueue new];
// 设置队列最大并发数为2
queue.maxConcurrentOperationCount = 2;
for (int i = 0; i<10; i++) {
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"%d-%@",i,[NSThread currentThread]);
}];
}
}
复制代码
打印:
2020-01-29 16:45:11.512510+0800 001---NSOperation初体验[2360:107175] 0-<NSThread: 0x6000023c2140>{number = 3, name = (null)}
2020-01-29 16:45:11.512505+0800 001---NSOperation初体验[2360:107173] 1-<NSThread: 0x6000023d1280>{number = 4, name = (null)}
2020-01-29 16:45:13.514845+0800 001---NSOperation初体验[2360:107172] 2-<NSThread: 0x6000023c4980>{number = 5, name = (null)}
2020-01-29 16:45:13.514883+0800 001---NSOperation初体验[2360:107174] 3-<NSThread: 0x6000023fc380>{number = 6, name = (null)}
2020-01-29 16:45:15.516537+0800 001---NSOperation初体验[2360:107173] 4-<NSThread: 0x6000023d1280>{number = 4, name = (null)}
2020-01-29 16:45:15.516537+0800 001---NSOperation初体验[2360:107172] 5-<NSThread: 0x6000023c4980>{number = 5, name = (null)}
2020-01-29 16:45:17.518188+0800 001---NSOperation初体验[2360:107173] 7-<NSThread: 0x6000023d1280>{number = 4, name = (null)}
2020-01-29 16:45:17.518188+0800 001---NSOperation初体验[2360:107174] 6-<NSThread: 0x6000023fc380>{number = 6, name = (null)}
2020-01-29 16:45:19.521176+0800 001---NSOperation初体验[2360:107172] 8-<NSThread: 0x6000023c4980>{number = 5, name = (null)}
2020-01-29 16:45:19.521176+0800 001---NSOperation初体验[2360:107174] 9-<NSThread: 0x6000023fc380>{number = 6, name = (null)}
复制代码
- (void)demo{
NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"请求token");
}];
NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"拿着token,请求数据1");
}];
NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"拿着数据1,请求数据2");
}];
//由于异步,很差控制,咱们借助依赖
[bo2 addDependency:bo1];
[bo3 addDependency:bo2];
//注意这里必定不要构成循环依赖 : 不会报错,可是全部操做不会执行
//[bo1 addDependency:bo3];
//waitUntilFinished 堵塞线程
[self.queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:NO];
NSLog(@"执行完了?我要干其余事");
}
复制代码
打印:
2020-01-29 16:47:41.371621+0800 002---NSOperation深刻浅出[2422:109106] 执行完了?我要干其余事
2020-01-29 16:47:41.872960+0800 002---NSOperation深刻浅出[2422:109157] 请求token
2020-01-29 16:47:42.374790+0800 002---NSOperation深刻浅出[2422:109156] 拿着token,请求数据1
2020-01-29 16:47:42.877750+0800 002---NSOperation深刻浅出[2422:109156] 拿着数据1,请求数据2
复制代码
/**
关于operationQueue的挂起,继续,取消
*/
- (void)demo1{
self.queue.name = @"Cooci";
self.queue.maxConcurrentOperationCount = 2;
for (int i=0; i< 20; i++) {
[self.queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%@-----%d",[NSThread currentThread],i);
}];
}
}
- (IBAction)pauseOrContinue:(id)sender {
if (self.queue.operationCount == 0) {
NSLog(@"当前没有操做,没有必要挂起和继续");
return;
}
// 一个细节 正在执行的操做没法挂起
if (self.queue.suspended) {
NSLog(@"当前是挂起状态,准备继续");
}else{
NSLog(@"当前为执行状态,准备挂起");
}
self.queue.suspended = !self.queue.isSuspended;
}
- (IBAction)cancel:(id)sender {
[self.queue cancelAllOperations];
}
复制代码
打印:
2020-01-29 16:52:44.854415+0800 002---NSOperation深刻浅出[2488:112001] <NSThread: 0x600001d57180>{number = 3, name = (null)}-----1
2020-01-29 16:52:44.854415+0800 002---NSOperation深刻浅出[2488:111998] <NSThread: 0x600001d1da40>{number = 4, name = (null)}-----0
2020-01-29 16:52:45.856950+0800 002---NSOperation深刻浅出[2488:111999] <NSThread: 0x600001d1f080>{number = 5, name = (null)}-----2
2020-01-29 16:52:45.856988+0800 002---NSOperation深刻浅出[2488:112000] <NSThread: 0x600001d04280>{number = 6, name = (null)}-----3
2020-01-29 16:52:46.277210+0800 002---NSOperation深刻浅出[2488:111955] 当前为执行状态,准备挂起
2020-01-29 16:52:46.857767+0800 002---NSOperation深刻浅出[2488:112001] <NSThread: 0x600001d57180>{number = 3, name = (null)}-----4
2020-01-29 16:52:46.857782+0800 002---NSOperation深刻浅出[2488:111999] <NSThread: 0x600001d1f080>{number = 5, name = (null)}-----5
2020-01-29 16:52:48.967113+0800 002---NSOperation深刻浅出[2488:111955] 当前是挂起状态,准备继续
2020-01-29 16:52:49.968072+0800 002---NSOperation深刻浅出[2488:112001] <NSThread: 0x600001d57180>{number = 3, name = (null)}-----6
2020-01-29 16:52:49.968087+0800 002---NSOperation深刻浅出[2488:111999] <NSThread: 0x600001d1f080>{number = 5, name = (null)}-----7
2020-01-29 16:52:50.969946+0800 002---NSOperation深刻浅出[2488:112000] <NSThread: 0x600001d04280>{number = 6, name = (null)}-----8
2020-01-29 16:52:50.969946+0800 002---NSOperation深刻浅出[2488:112001] <NSThread: 0x600001d57180>{number = 3, name = (null)}-----9
2020-01-29 16:52:51.465420+0800 002---NSOperation深刻浅出[2488:111955] 所有取消
2020-01-29 16:52:51.973286+0800 002---NSOperation深刻浅出[2488:111999] <NSThread: 0x600001d1f080>{number = 5, name = (null)}-----10
2020-01-29 16:52:51.973297+0800 002---NSOperation深刻浅出[2488:112001] <NSThread: 0x600001d57180>{number = 3, name = (null)}-----11
复制代码