zoukankan      html  css  js  c++  java
  • Operation(Swift)

    介绍:

    NSOperation需要配合NSOperationQueue来实现多线程。因为默认情况下,NSOperation单独使用时系统同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行。

    NSOperation是一个抽象类,并不具备封装操作的能力,必须使用它的子类;

    因为NSOperation是基于GCD的,那么使用起来也和GCD差不多,其中,NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列。NSOperation实现多线程的使用步骤分为三步:

    a. 创建任务:先将需要执行的操作封装到一个NSOperation对象中。

    b. 创建队列:创建NSOperationQueue对象。

    c. 将任务加入到队列中:然后将NSOperation对象添加到NSOperationQueue中。

    之后呢,系统就会自动将NSOperationQueue中的NSOperation取出来,在新线程中执行操作。


    NSOperation是个抽象类,并不能封装任务。我们只有使用它的子类来封装任务。我们有三种方式来封装任务。

    使用子类NSInvocationOperation
    使用子类NSBlockOperation
    定义继承自NSOperation的子类,通过实现内部相应的方法来封装任务。
    在不使用NSOperationQueue,单独使用NSOperation的情况下系统同步执行操作,下面我们学习以下任务的三种创建方式。

    NSInvocationOperation 

    OC版

     1 - (void)testB {
     2 //    创建NSInvocationOperation对象
     3     NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
     4     
     5     // 调用start方法开始执行操作
     6     [op start];
     7 }
     8 
     9 - (void)run
    10 {
    11     NSLog(@"当前的线程为--%@", [NSThread currentThread]);
    12 }

    结果:

    2018-01-30 18:46:59.139360+0800 RAC[21774:638747] 当前的线程为--<NSThread: 0x60c000070c00>{number = 1, name = main}
    

    BlockOperation:

    Swift 版

    只要BlockOperation封装的操作数 > 1,就会异步执行操作,但是不会无限制的创建线程

    let blockOpe = BlockOperation()
            blockOpe.queuePriority = .veryHigh
            blockOpe.addExecutionBlock {
                print("BlockOperation执行了",Thread.current)
            }
            blockOpe.addExecutionBlock {
                print("BlockOperation2执行了",Thread.current)
            }
            blockOpe.start()  //开始执行
            //blockOpe.cancel() //取消

    结果:

    OC 版

    1     NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    2         
    3         NSLog(@"-当前的线程为-%@",[NSThread currentThread]);
    4     }];
    5     
    6     [op start];

    结果:

    在没有使用NSOperationQueue、单独使用NSBlockOperation的情况下,NSBlockOperation也是在主线程执行操作,并没有开启新线程。

    addExecutionBlock

     1 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
     2         // 在主线程
     3         NSLog(@"线程1------%@", [NSThread currentThread]);
     4     }];
     5     
     6     // 添加额外的任务(在子线程执行)
     7     [op addExecutionBlock:^{
     8         NSLog(@"线程2------%@", [NSThread currentThread]);
     9     }];
    10     [op addExecutionBlock:^{
    11         NSLog(@"线程3------%@", [NSThread currentThread]);
    12     }];
    13     [op addExecutionBlock:^{
    14         NSLog(@"线程4------%@", [NSThread currentThread]);
    15     }];
    16     
    17     [op start];

    结果:

    blockOperationWithBlock:方法中的操作是在主线程中执行的,而addExecutionBlock:方法中的操作是在其他线程中执行的


    NSOperationQueue

    和GCD中的并发队列、串行队列略有不同的是:NSOperationQueue一共有两种队列:主队列、其他队列。其中其他队列同时包含了串行、并发功能。

    创建队列

    主队列

    获取主队列以及添加操作到队列,主队列是串行队列,无论是否设置最大并行数量都是在当前线程执行,不会创建新的线程

    凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行。

    1    let queue = OperationQueue.main
    2         queue.addOperation {
    3             for _ in 0...50000{
    4                 print("OperationQueue1执行了",Thread.current)
    5             }
    6         }

    结果:

    其他队列

    添加到这种队列中的任务(NSOperation),就会自动放到子线程中执行。同时包含了:串行、并发功能

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

    a. 

    将任务加入到队列中

    同时具备串行和并发的功能,默认情况下是并发的,可以手动设置为串行队列,通过设置最大并发数属性来更改

    1 c func racsTest() {
    2         let queue = OperationQueue()
    3         queue.maxConcurrentOperationCount = 10
    4         queue.addOperation {
    5             for _ in 0...50000{
    6                 print("OperationQueue1执行了",Thread.current)
    7             }
    8         }
    9         //addOperation已经在内部执行了start方法了

    结果:


    最大并发数

    a. 取消队列中的所有任务,除了正在执行的任务,一旦被取消,就不能回复之前的操作。

    b. 将最大操作数的值设置为1,可以实现任务的串行效果,但是要注意的是,并不是只开一条子线程(通常会开两条子线程,循环回收复用)

    c. 最大并发数6以内,不要开太多,因为虽然任务是在子线程进行处理的,但是cpu处理这些过多的子线程可能会影响UI,让UI卡顿。

    1 queue.maxConcurrentOperationCount=2;

    队列的取消

    当前正在处于执行状态的任务是不能够取消的,只能取消等待的任务

    1 queue.cancelAllOperations()

    队列的挂起和恢复

    当前正在处于执行状态的任务是不能够暂停的,只能暂停等待的任务

    1 queue.isSuspended = true    //暂停队列
    2 queue.isSuspended = false  //恢复队列

    等待Options完成

    如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成

    1 [operation waitUntilFinished];

    使用NSOperationQueue的waitUntilAllOperationsAreFinished方法

    1 // 阻塞当前线程,等待queue的所有操作执行完毕  
    2 [queue waitUntilAllOperationsAreFinished];

    添加NSOperation的依赖对象

    当某个NSOperation对象依赖于其它NSOperation对象的完成时,就可以通过addDependency方法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作,当前NSOperation对象才会开始执行操作。另外,通过removeDependency方法来删除依赖对象。
     
     1 let queue = OperationQueue()
     2         //设置依赖关系
     3         let blockOpe1 = BlockOperation()
     4         blockOpe1.addExecutionBlock {
     5             print("blockOpe1执行了")
     6         }
     7         let blockOpe2 = BlockOperation()
     8         blockOpe2.addExecutionBlock {
     9             print("blockOpe2执行了")
    10         }
    11         blockOpe1.addDependency(blockOpe2)
    12         queue.addOperation(blockOpe1)
    13         queue.addOperation(blockOpe2)
    14 //        可以看出,先执行blockOpe2,再执行blockOpe1
    15         

    结果:


    修改Operations的执行顺序

    默认所有operation都拥有“普通”优先级,不过可以通过setQueuePriority:方法来提升或降低operation对象的优先级。

     1         let queue = OperationQueue()
     2         queue.maxConcurrentOperationCount = 3
     3 
     4         let blockOpe = BlockOperation()
     5         blockOpe.addExecutionBlock {
     6             print("blockOpe------------>执行了")
     7         }
     8         blockOpe.queuePriority = .veryHigh
     9         
    10         let blockOpe1 = BlockOperation()
    11         blockOpe1.addExecutionBlock {
    12             print("blockOpe1------------>执行了")
    13         }
    14         blockOpe1.queuePriority = .low
    15         
    16         queue.addOperation(blockOpe1)
    17         queue.addOperation(blockOpe)

    结果:


    定义NSOperation的子类

     1 //    定义一个继承自NSOperation的子类,重写main方法
     2 //    YSCOperation.h
     3 #import <Foundation/Foundation.h>
     4 
     5 @interface YSCOperation : NSOperation
     6 
     7 @end
     8 
     9 
    10 //    YSCOperation.m
    11 
    12 #import "YSCOperation.h"
    13 
    14 @implementation YSCOperation
    15 /**
    16  * 需要执行的任务
    17  */
    18 
    19 - (void)main
    20 {
    21     for (int i = 0; i < 2; ++i) {
    22         NSLog(@"--i---%@",[NSThread currentThread]);
    23     }    
    24 }
    25 
    26 @end
    27 
    28 
    29 
    30 //    导入头文件YSCOperation.h
    31 // 创建YSCOperation
    32 YSCOperation *op1 = [[YSCOperation alloc] init];
    33 
    34 [op1 start];

    结果:

    --0---<NSThread: 0x6080000779c0>{number = 1, name = main}
     --1---<NSThread: 0x6080000779c0>{number = 1, name = main}

    在没有使用NSOperationQueue、单独使用自定义子类的情况下,是在主线程执行操作,并没有开启新线程。

  • 相关阅读:
    mybatis-generator的坑
    log框架集成
    状压dp
    GYM 101350 G
    Wannafly挑战赛23 A 字符串
    基础
    ACM Changchun 2015 A. Too Rich
    最大子串和
    memset
    int long long 的范围
  • 原文地址:https://www.cnblogs.com/EchoHG/p/8378843.html
Copyright © 2011-2022 走看看