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

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");
建立依赖关系 : 要将操作间的依赖建立好了之后,再添加到队列中





猜你喜欢

转载自blog.csdn.net/mr_tangit/article/details/80613615