iOS接下来要研究的知识点将来跳槽用夯实基础

iOS开发之多线程(4)—— Operation

2020-07-09  本文已影响0人  看影成痴

文集

iOS开发之多线程(1)—— 概述
iOS开发之多线程(2)—— Thread
iOS开发之多线程(3)—— GCD
iOS开发之多线程(4)—— Operation
iOS开发之多线程(5)—— Pthreads
iOS开发之多线程(6)—— 线程安全与各种锁

目录

版本
简介

基本用法

  1. 不使用队列
  2. 使用队列
    2.1 四种添加操作(任务)方法
    2.2 其他属性方法
    2.3 线程间通信

版本

Xcode 11.5
Swift 5.2.2

简介

我们先来讨论相关理论知识点, 代码部分统一放到后文.

Operation基于GCD封装, 完全面向对象. 对应于GCD, Operation也有任务和队列的概念, 只不过在Operation中任务(block)被称为操作(operation).

Operation支持以下关键功能:

  1. 操作之间添加依赖
  2. 使用KVO监听操作的执行状态
  3. 对操作进行优先级排序
  4. 取消操作

操作

Operation是一个抽象类, 代表一个任务. 通常我们使用它的子类NSInvocationOperation或NSBlockOperation来编写任务代码. 当然也可以直接使用Operation, 不过需要重写main方法, 在main里面编写任务代码.

3种创建方式:

  1. NSInvocationOperation (swift不支持): 此类调用选择器方法(selector), 在方法里面编写任务代码.
  2. NSBlockOperation (swift对应BlockOperation): 此类采用block方式, 在block中编写任务代码.
  3. NSOperation (swift对应Operation): 需要重写main方法, 在main里面编写任务代码.

2种执行方式:

  1. 不添加到队列, 手动调用operation的start方法.
  2. 添加到队列, 系统自动调用start方法.

具体如何使用请看下小节.

同步 & 异步

在GCD中, 同步和异步分别对应dispatch_sync和dispatch_async方法.
在Operation中, 没有这种方法.

苹果文档
如果计划手动执行操作对象,而不是将其添加到队列中,则可以将操作设计为以同步或异步方式执行。默认情况下,操作对象是同步的。当start()直接从代码中调用同步操作的方法时,该操作将在当前线程中立即执行。
默认情况下,操作对象以同步方式执行-也就是说,它们在调用其start方法的线程中执行其任务。
为了获得最佳性能,您应该将操作设计为尽可能异步,使应用程序在执行操作时可以自由地做其他工作。

注意: 与GCD的同步执行不同, 这里虽然设置了等待, 但是一般不在当前线程执行, 而是新开一个线程来执行, 但会保证队列里的所有任务完成后才继续往下执行.

队列

队列 (Operation Queue)有两种: 主队列和非主队列 (自定义队列).

添加操作(任务)到队列有四种方式:

  1. addOperation:
    添加一个现有的Operation (或者其子类).
  2. addOperations:waitUntilFinished:
    可添加多个现有的Operation (或者其子类), 可设置等待所有操作完成后方可继续往下执行.
  3. addOperationWithBlock:
    直接添加一个block
  4. addBarrierBlock:
    添加栅栏, 顺带一个任务. 等栅栏前的所有任务都执行完, 再执行本栅栏的任务, 起到隔离同步等待的目的.

串行 & 并行

主队列是串行队列. 自定义队列默认是并发队列, 但可通多设置最大并发数(maxConcurrentOperationCount)来控制队列是串行还是并发.

maxConcurrentOperationCount
-1, 默认值, 并发队列;
=0, 不执行任何操作;
=1, 串行队列;
<0, 除-1默认值外, 其他负值均报错;
>1, 并发队列, 如果数值过大, 最终并发数由系统决定.

基本用法

1. 不使用队列

OC

#pragma mark - NSInvocationOperation (调用selector方法)

// 使用 NSInvocationOperation
- (void)useInvocationOperation {
    
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doInvocationOperation) object:nil];
    [operation start];
}

// 任务
- (void)doInvocationOperation {
    
    NSLog(@"%s, thread:%@", __func__, [NSThread currentThread]);
}

//---------------------------------------------------------------------------------------------------
//log:
//-[ViewController doInvocationOperation], thread:<NSThread: 0x6000017ec2c0>{number = 1, name = main}


#pragma mark - NSBlockOperation (使用block)

// 使用 NSBlockOperation
- (void)useBlockOperation {
    
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"%s, thread:%@", __func__, [NSThread currentThread]);
    }];
    
    [operation start];
}

//---------------------------------------------------------------------------------------------------
//log:
//-[ViewController useBlockOperation]_block_invoke, thread:<NSThread: 0x600000760100>{number = 1, name = main}


#pragma mark - 自定义Operation (重写main)

- (void)useCustomOperation {
    
    MyOperation *operation = [[MyOperation alloc] init];
    [operation start];
}

//---------------------------------------------------------------------------------------------------
//log:
//-[MyOperation main], thread:<NSThread: 0x600000618280>{number = 1, name = main}
MyOperation.m

@implementation MyOperation

- (void)main {
    
    NSLog(@"%s, thread:%@", __func__, [NSThread currentThread]);
}

@end

在自定义Operation中, 调用start方法后, 系统会执行多项安全检查, 最终会调用main方法.

Swift

//MARK: - 使用 BlockOperation (block)
@objc func useBlockOperation() {
    
    let operation = BlockOperation.init {
        print("\(#function), thread:\(Thread.current)")
    }
    operation.start()
}
    
//---------------------------------------------------------------------------------------------------
//log:
//useBlockOperation(), thread:<NSThread: 0x600003f78000>{number = 1, name = main}
    

//MARK: - 使用 自定义Operation (重写main)
@objc func useCustomOperation() {
    
    let operation = CustomOperation.init()
    operation.start()
}
    
//---------------------------------------------------------------------------------------------------
//log:
//main(), thread:<NSThread: 0x600003ec8cc0>{number = 1, name = main}
class CustomOperation: Operation {
    
    override func main() {
        print("\(#function), thread:\(Thread.current)")
    }
}

2. 使用队列

2.1 四种添加操作(任务)方法

OC

    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];

    // 1 addOperation:
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op1 thread:%@", [NSThread currentThread]);
    }] ;
    [operationQueue addOperation:op1];

    // 2 addOperations:waitUntilFinished:
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op2 thread:%@", [NSThread currentThread]);
    }] ;
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op3 thread:%@", [NSThread currentThread]);
    }] ;
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op4 thread:%@", [NSThread currentThread]);
    }] ;
    [operationQueue addOperations:@[op2, op3, op4] waitUntilFinished:YES];
    NSLog(@"queue finished");

    // 3 addOperationWithBlock:
    [operationQueue addOperationWithBlock:^{
        NSLog(@"block thread:%@", [NSThread currentThread]);
    }];

    // 4 addBarrierBlock:
    [operationQueue addBarrierBlock:^{
        NSLog(@"barrier thread:%@", [NSThread currentThread]);
    }];

//---------------------------------------------------------------------------------------------------
//log:
//op2 thread:<NSThread: 0x600002ec7c80>{number = 6, name = (null)}
//op4 thread:<NSThread: 0x600002eaf180>{number = 4, name = (null)}
//op1 thread:<NSThread: 0x600002ed77c0>{number = 5, name = (null)}
//op3 thread:<NSThread: 0x600002eaf140>{number = 3, name = (null)}
//queue finished
//block thread:<NSThread: 0x600002ec7c80>{number = 6, name = (null)}

Swift

    let operationQueue = OperationQueue.init()
        
    // 1 addOperation(_ op: Operation)
    let op1 = BlockOperation.init {
        print("op1, thread:\(Thread.current)")
    }
    operationQueue.addOperation(op1)
    
    
    // 2 addOperations(_ ops: [Operation], waitUntilFinished wait: Bool)
    let op2 = BlockOperation.init {
        print("op2, thread:\(Thread.current)")
    }
    let op3 = BlockOperation.init {
        print("op3, thread:\(Thread.current)")
    }
    let op4 = BlockOperation.init {
        print("op4, thread:\(Thread.current)")
    }
    operationQueue.addOperations([op2, op3, op4], waitUntilFinished: true)
    
    
    // 3 addOperation(_ block: @escaping () -> Void)
    operationQueue.addOperation {
        print("block, thread:\(Thread.current)")
    }
    

    // 4 addBarrierBlock(_ barrier: @escaping () -> Void)
    operationQueue.addBarrierBlock {
        print("barrier, thread:\(Thread.current)")
    }
        
//---------------------------------------------------------------------------------------------------
log:
op3, thread:<NSThread: 0x600002399200>{number = 4, name = (null)}
op1, thread:<NSThread: 0x6000023c34c0>{number = 5, name = (null)}
op2, thread:<NSThread: 0x600002398f80>{number = 6, name = (null)}
op4, thread:<NSThread: 0x6000023d42c0>{number = 7, name = (null)}
block, thread:<NSThread: 0x600002398f80>{number = 6, name = (null)}
barrier, thread:<NSThread: 0x600002398f80>{number = 6, name = (null)}
2.2 其他属性方法

maxConcurrentOperationCount

- (void)setOperationCount {
    
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
    
    NSLog(@"start");
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op1 thread:%@", [NSThread currentThread]);
    }] ;
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op2 thread:%@", [NSThread currentThread]);
    }] ;
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op3 thread:%@", [NSThread currentThread]);
    }] ;
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op4 thread:%@", [NSThread currentThread]);
    }] ;
    
    /**
     -1, 默认值, 并发队列;
     =0, 不执行任何操作;
     =1, 串行队列;
     <0, 除-1默认值外, 其他负值均报错;
     >1, 并发队列, 如果数值过大, 最终并发数由系统决定.
     */
    operationQueue.maxConcurrentOperationCount = 1;
    [operationQueue addOperations:@[op1, op2, op3, op4] waitUntilFinished:YES];
    
    NSLog(@"end");
}

//---------------------------------------------------------------------------------------------------
log:
start
op1 thread:<NSThread: 0x600000b181c0>{number = 5, name = (null)}
op2 thread:<NSThread: 0x600000b75f80>{number = 6, name = (null)}
op3 thread:<NSThread: 0x600000b75f80>{number = 6, name = (null)}
op4 thread:<NSThread: 0x600000b75f80>{number = 6, name = (null)}
end

我们看到:
由于是串行队列, 没有添加依赖和设置优先级, 所以会按照1~4顺序执行;
又因为设置了等待(waitUntilFinished:YES), 所以end最后才打印;
开不开新线程, 由系统决定.

添加依赖

addDependency:

设置优先级

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

取消队列里所有任务

- (void)cancelAllOperations;
2.3 线程间通信
- (void)callMainQueue {
    
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
    
    [operationQueue addOperationWithBlock:^{
        
        NSLog(@"block, thread:%@", [NSThread currentThread]);
        
        // call main queue
        [NSOperationQueue.mainQueue addOperationWithBlock:^{
            
            NSLog(@"main, thread:%@", [NSThread currentThread]);
        }];
    }];
}

//---------------------------------------------------------------------------------------------------
log:
block, thread:<NSThread: 0x600003ee0580>{number = 6, name = (null)}
main, thread:<NSThread: 0x600003ea0240>{number = 1, name = main}

demo
https://github.com/LittleLittleKang/KKThreadsDemo

上一篇 下一篇

猜你喜欢

热点阅读