iOS 开发 多线程详解之NSOperation实现多线程

来源:互联网 发布:柏拉图交友软件 编辑:程序博客网 时间:2024/05/22 16:53

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封装的操作自动放到一条对应的新线程中执行.

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;

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;

子类: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;
//和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;

基本用法

- (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];    }}

这里写图片描述

开发中的用法

  • NSOperationQueue只有一种类型.就是并发队列.
  • 在实际开发时,如果要使用到NSOperationQueue,可以直接定义成全局的队列
@interface ViewController ()/// 开发中会定义一个全局的并发队列@property (nonatomic, strong) NSOperationQueue *queue;@end
//懒加载- (NSOperationQueue *)queue {    if (_queue == nil) {        _queue = [[NSOperationQueue alloc] init];    }    return _queue;}
#pragma mark - OP的简写- (void)OPDemo3 {    [self.queue addOperationWithBlock:^{        NSLog(@"子线程中执行的操作 %@",[NSThread currentThread]);    }];}

线程间通信

#pragma mark - OP的线程间的通信- (void)OPDemo4{    // 在子线程执行下载的操作    [self.queue addOperationWithBlock:^{        NSLog(@"子线程中执行下载的操作 %@",[NSThread currentThread]);        // 如果下载结束,就回到主线程刷新UI        [[NSOperationQueue mainQueue] addOperationWithBlock:^{            NSLog(@"刷新UI %@",[NSThread currentThread]);        }];    }];}

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];    }}

执行的结果 : 任务是两个两个的执行.
这里写图片描述

队列的暂停继续和取消全部

  • (BOOL)isSuspended;暂停和继续队列的属性.
  • YES代表暂停队列,NO代表恢复队列.
  • cancelAllOperations : 取消队列中的全部操作.
  • cancel : 取消队列中的单个操作.

这里写图片描述

@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);}

这里写图片描述
这里写图片描述

  • 将队列挂起之后,队列中的操作就不会被调度,但是正在执行的操作不受影
  • 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];}

这里写图片描述

操作间依赖

需求 : 登陆–>付费–>下载–>通知用户

#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]);    }];}

建立依赖关系 : 不能循环建立操作间依赖关系.否则,队列不调度操作执行

// 操作2依赖操作1[op2 addDependency:op1];[op3 addDependency:op2];[op4 addDependency:op3];// 不能循环依赖 : 操作不会被调度// [op1 addDependency:op4];// waitUntilFinished : 是否等到指定的操作执行结束再执行后面的代码[self.queue addOperations:@[op1,op2,op3,op4] waitUntilFinished:NO];// 验证 waitUntilFinishedNSLog(@"end");

建立依赖关系 : 操作间可以跨队列建立依赖关系

// 操作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];// 验证 waitUntilFinishedNSLog(@"end");建立依赖关系 : 要将操作间的依赖建立好了之后,再添加到队列中
0 0
原创粉丝点击