NSOperation简介
@interface NSOperation : NSObject
1.他是一个抽象类,无法直接使用.但是我们可以使用它的子类.作为父类约束子类共有的属性和方法
2.子类 - 操作默认是异步的.
NSBlockOperation
NSInvocationOperation
自定义NSOperation
3.队列 - 默认是并发的.
@interface NSOperationQueue : NSObject
4.总结
GCD的核心 : 将任务添加到队列
NSOperation的核心 : 将操作添加到队列
5.使用步骤
1.先将需要执行的操作封装到一个NSOperation对象中.创建NSOperation对象.
2.将NSOperation对象添加到NSOperationQueue中.
3.NSOperationQueue会自动将NSOperation取出来.
4.将取出的NSOperation封装的操作自动放到一条对应的新线程中执行.
- 1
- 2
- 3
- 4
NSOperation的属性和方法
//
- (void)start;
//
- (void)main;
//操作取消
@property (readonly, getter=isCancelled) BOOL cancelled;
- (void)cancel;
//操作执行
@property (readonly, getter=isExecuting) BOOL executing;
//操作结束
@property (readonly, getter=isFinished) BOOL finished;
//并发操作
@property (readonly, getter=isConcurrent) BOOL concurrent;
//异步操作
@property (readonly, getter=isAsynchronous) BOOL asynchronous ;
//操作依赖关系的添加和删除--操作可以'跨队列'依赖
- (void)addDependency:(NSOperation *)op;
- (void)removeDependency:(NSOperation *)op;
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
//队列优先级
@property NSOperationQueuePriority queuePriority;
//线程优先级
@property double threadPriority ;
//服务质量
@property NSQualityOfService qualityOfService ;
//操作属性名
@property (nullable, copy) NSString *name;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
NSOperationQueue的属性和方法
//队列添加操作
- (void)addOperation:(NSOperation *)op;
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;
//最常用的添加操作到队列的方法
- (void)addOperationWithBlock:(void (^)(void))block ;
//多个操作
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
//队列的操作计数--保存未执行/未执行完的操作个数
@property (readonly) NSUInteger operationCount ;
//队列的最大并发数--限制**同时执行**的操作数.
@property NSInteger maxConcurrentOperationCount;
//队列的挂起--暂停(正在执行的操作无法被暂停)
@property (getter=isSuspended) BOOL suspended;
//队列的名称
@property (nullable, copy) NSString *name ;
//操作的优先级 : 不决定顺序,只改变概率
@property NSQualityOfService qualityOfService ;
//取消所有操作--正在执行的操作无法取消
- (void)cancelAllOperations;
- (void)waitUntilAllOperationsAreFinished;
//当前队列
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue ;
//最队列
@property (class, readonly, strong) NSOperationQueue *mainQueue;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
子类:NSInvocationOperation
//基本没什么功能,很少有人用
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
- (instancetype)initWithInvocation:(NSInvocation *)inv;
@property (readonly, retain) NSInvocation *invocation;
@property (nullable, readonly, retain) id result;
- 1
- 2
- 3
- 4
- 5
//和NSThread用法有些类似,都需要alloc init 去添加target和selector---操作和要执行的任务是分开的
- (void)opDemo3
{
// 队列 : 默认是并发的
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 循环的向队列中添加10个操作
for (int i = 0; i < 10; i++) {
// 操作对象 : OP中的操作对象默认是异步执行
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo:) object:@(i)];
// 将操作添加到队列
[queue addOperation:op];
}
}
- (void)demo:(id)parram
{
// 查看当前线程
NSLog(@"%@ %@",parram,[NSThread currentThread]);
}
执行效果 : 会开启多条线程,不是顺序执行.与GCD中并发队列&异步执行效果一样
- 队列 : 默认是并发的
子类:NSBlockOperation
属性
//直接在block块里添加需要执行的操作,很方便
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
- (void)addExecutionBlock:(void (^)(void))block;
@property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;
- 1
- 2
- 3
- 4
基本用法
- (void)OPDemo2
{
// 创建队列 : 队列默认是并发的
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
for (NSInteger i = 0; i < 100; i++) {
// 创建操作 : 默认是异步的
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%zd %@",i,[NSThread currentThread]);
}];
// 把操作添加到队列
[queue addOperation:op];
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
开发中的用法
- NSOperationQueue只有一种类型.就是并发队列.
- 在实际开发时,如果要使用到NSOperationQueue,可以直接定义成全局的队列
@interface ViewController ()
/// 开发中会定义一个全局的并发队列
@property (nonatomic, strong) NSOperationQueue *queue;
@end
- 1
- 2
- 3
- 4
- 5
- 6
//懒加载
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [[NSOperationQueue alloc] init];
}
return _queue;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
#pragma mark - OP的简写
- (void)OPDemo3 {
[self.queue addOperationWithBlock:^{
NSLog(@"子线程中执行的操作 %@",[NSThread currentThread]);
}];
}
- 1
- 2
- 3
- 4
- 5
- 6
线程间通信
#pragma mark - OP的线程间的通信
- (void)OPDemo4
{
// 在子线程执行下载的操作
[self.queue addOperationWithBlock:^{
NSLog(@"子线程中执行下载的操作 %@",[NSThread currentThread]);
// 如果下载结束,就回到主线程刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"刷新UI %@",[NSThread currentThread]);
}];
}];
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
maxConcurrentOperationCount 最大并发数
NSOperation封装的是GCD也有可调度线程池,会重用线程
@interface ViewController ()
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [[NSOperationQueue alloc] init];
// 设置队列的最大并发数 : 保证了队列最多可以同时调度2个操作同时执行,"间接"控制了线程的数量
_queue.maxConcurrentOperationCount = 2;
}
return _queue;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self OPDemo1];
}
- (void)OPDemo1 {
for (NSInteger i = 0; i < 50; i++) {
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 模拟延迟 : 让设置最大并发数之后的执行效果更佳的明显
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%zd %@",i,[NSThread currentThread]);
}];
[self.queue addOperation:op];
}
}
@interface ViewController ()@property (nonatomic, strong) NSOperationQueue *queue;@end@implementation ViewController- (NSOperationQueue *)queue { if (_queue == nil) { _queue = [[NSOperationQueue alloc] init]; // 设置队列的最大并发数 : 保证了队列最多可以同时调度2个操作同时执行,"间接"控制了线程的数量 _queue.maxConcurrentOperationCount = 2; } return _queue;}#pragma mark - 暂停/* 1.正在执行的操作无法被暂停 2.队列的操作计数 : operationCount;保存的是没有执行完的操作,已经执行完的操作不会被计数在内 3.一旦先把队列挂起,再往里面添加操作,这个操作是可以成功的添加进去的,但是不会被调度执行 */- (IBAction)zanting:(id)sender { // 当队列里面没有操作时,就不让暂停/不让挂起队列 if (self.queue.operationCount == 0) { return; } // 是队列暂停调度任务 self.queue.suspended = YES; NSLog(@"暂停 %tu",self.queue.operationCount);}
#pragma mark - 继续- (IBAction)jixu:(id)sender { // 是队列继续调度任务 self.queue.suspended = NO; NSLog(@"继续 %tu",self.queue.operationCount);}
#pragma mark - 取消全部// 正在执行的操作不会被取消// 提示 : 如果非要取消正在执行的操作,就需要自定义NSOperation- (IBAction)cancelAll:(id)sender { // 取消队列里面所有的操作 [self.queue cancelAllOperations]; // 提示 : 取消全部的操作是有延迟的 // 休眠的目的是为了演示延迟操作 [NSThread sleepForTimeInterval:2.0]; NSLog(@"取消全部 %tu",self.queue.operationCount);}
一旦调用的 cancelAllOperations方法,队列中的操作,都会被移除,正在执行的操作除外.
- 正在执行的操作取消不了,如果要取消,需要自定义队列.
操作优先级和监听操作完成回调
- 操作的优先级 : qualityOfService
无法决定操作执行的先后顺序的,决定的是操作有更多的机会被队列调度执行. - 监听操作完成的回调 : @property (nullable, copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);
当操作执行结束之后,就会回调,是在子线程中执行的.
- (void)opDemo
{
// 操作1
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 10; i++) {
// 查看当前线程
NSLog(@"op1 %@",[NSThread currentThread]);
}
}];
//设置操作的优先级
op1.qualityOfService = NSQualityOfServiceUserInteractive;
// 当操作执行结束之后,就会回调,是在子线程中执行的
[op1 setCompletionBlock:^{
// 查看当前线程
NSLog(@"操作结束了 %@",[NSThread currentThread]);
}];
[self.queue addOperation:op1];
// 操作2
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 10; i++) {
// 查看当前线程
NSLog(@"op2 %@",[NSThread currentThread]);
}
}];
op2.qualityOfService = NSQualityOfServiceBackground;
[self.queue addOperation:op2];
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
操作间依赖
需求 : 登陆–>付费–>下载–>通知用户
#pragma mark - 操作依赖
- (void)dependency
{
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"登陆 %@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"付费 %@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载 %@",[NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"通知用户 %@",[NSThread currentThread]);
}];
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
建立依赖关系 : 不能循环建立操作间依赖关系.否则,队列不调度操作执行
// 操作2依赖操作1
[op2 addDependency:op1];
[op3 addDependency:op2];
[op4 addDependency:op3];
// 不能循环依赖 : 操作不会被调度
// [op1 addDependency:op4];
// waitUntilFinished : 是否等到指定的操作执行结束再执行后面的代码
[self.queue addOperations:@[op1,op2,op3,op4] waitUntilFinished:NO];
// 验证 waitUntilFinished
NSLog(@"end");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
建立依赖关系 : 操作间可以跨队列建立依赖关系
// 操作2依赖操作1
[op2 addDependency:op1];
[op3 addDependency:op2];
[op4 addDependency:op3];
// 不能循环依赖 : 操作不会被调度
// [op1 addDependency:op4];
// waitUntilFinished : 是否等到指定的操作执行结束再执行后面的代码
[self.queue addOperations:@[op1,op2,op3] waitUntilFinished:NO];
// 通知用户的操作在主线程中执行
// 操作可以跨队列依赖
[[NSOperationQueue mainQueue] addOperation:op4];
// 验证 waitUntilFinished
NSLog(@"end");
建立依赖关系 : 要将操作间的依赖建立好了之后,再添加到队列中