• iOS 多线程之 NSOperation 的基本使用


    1.NSOperation,NSOperationQueue 简介

    NSOperation,NSOperationQueue是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。

    为什么使用NSOperation NSoperationQueue

    1.可以添加完整的代码块 在操作完成后执行

    2.添加操作之间的依赖关系 方便的控制执行顺序

    3.设定操作执行的优先级

    4.可以很方便的取消一个操作的执行

    5.使用KVO观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled

    二 NSOperation NSOperationQueue 操作和操作队列

    既然是基于GCD的更高一层的封装。那么,GCD中的一些概念同样适用于NSOperation,NSOperationQueue。在NSoperation和NSOPerationQUeue中也有类似的任务(操作)和队列(操作队列)的概念。

    操作

    执行操作的意思 换句话说就是你在线程中执行的那段代码

    在GCD中是放在Block中执行的。在NSoperation中 我们使用 NSOperation 子类 NSInvocationOperationNSBlockOperation,或者自定义子类来封装操作。

    操作队列

    这里的队列指操作队列 即用来存放操作的队列 不同于GCD中的调度队列FIFO(先进先出)的原则 NSOperationQueue 对于添加到队列中的操作,首先进入准备就绪的状态(就绪状态取决于操作之间的依赖关系),然后进入就绪状态的操作的开始执行顺序(非结束执行顺序)由操作之间相对的优先级决定(优先级是操作对象自身的属性)。

    操作队列通过设置最大并发操作数(maxConcurrentOperationCount)来控制并发、串行。

    NSOperationQueue 为我们提供了两种不同类型的队列:主队列和自定义队列。主队列运行在主线程之上,而自定义队列在后台执行。

    三 NSOperation NSOperationQueue 的使用步骤

    NSOperation需要配合NSOperationQueue 来实现多线程。因为默认情况下NSOperation 单独使用时系统同步执行操作,配合 NSOperationQueue 我们能更好的实现异步执行。

    NSOPeration 实现多线程的使用步骤分为三步:

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

    2.创建队列:创建NSOperationQueue 对象

    3.将操作加入到队列中:将NSOperation对象添加到NS OperationQueue对象中

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

    四 NSOPeration的使用

    不添加到NSOPerationQueue 中 只封装操作的时候

    - (void)blockOPeration {
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op1 --- %@",[NSThread currentThread]);
        }];
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op2 --- %@",[NSThread currentThread]);
        }];
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op3 --- %@",[NSThread currentThread]);
        }];
        
        //追加任务
        /*
         *如果没加入到队列中 默认是在主线程中执行的 但是如果一个操作中添加了多个任务(任务数量大于1) 会开启线程执行 
        但不一定是子线程 有可能是主线程 NSInvocationOperation也是这种情况
    */ [op3 addExecutionBlock:^{ NSLog(@"op4 ---- %@",[NSThread currentThread]); }]; [op3 addExecutionBlock:^{ NSLog(@"op5 ---- %@",[NSThread currentThread]); }]; [op3 addExecutionBlock:^{ NSLog(@"op6 ---- %@",[NSThread currentThread]); }]; [op1 start]; [op2 start]; [op3 start]; }
    2018-03-08 23:05:43.358904+0800 NSOperationDemo[899:52772] op1 --- <NSThread: 0x60c000066e80>{number = 1, name = main}
    2018-03-08 23:05:43.359121+0800 NSOperationDemo[899:52772] op2 --- <NSThread: 0x60c000066e80>{number = 1, name = main}
    2018-03-08 23:05:43.359699+0800 NSOperationDemo[899:52772] op5 ---- <NSThread: 0x60c000066e80>{number = 1, name = main}
    2018-03-08 23:05:43.359701+0800 NSOperationDemo[899:52966] op3 --- <NSThread: 0x60000007df40>{number = 3, name = (null)}
    2018-03-08 23:05:43.359714+0800 NSOperationDemo[899:53301] op4 ---- <NSThread: 0x60000007de00>{number = 4, name = (null)}
    2018-03-08 23:05:43.359719+0800 NSOperationDemo[899:53306] op6 ---- <NSThread: 0x604000079840>{number = 5, name = (null)}

    NSOperationQueue 的使用

    - (void)invocationOPerationWithQueue {
        //创建操作 封装任务
        //第一个参数 目标对象 self
        //第二个参数 调用方法的名称
        //第三个参数 前面方法需要接受的参数
        NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
        NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
        NSInvocationOperation *operation3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task3) object:nil];
        //创建队列
        /*
         GCD
         串行队列 crate & 主队列
         并发队列 create & 全局并发队列
         NSOperation:
         主队列 [NSOperationQueue mainQueue]; 是串行队列
         非主队列 [[NSOperationQueue alloc] init]; 同时具备了 串行队列和并发队列的功能
         默认情况下 非主队列是一个并发队列
         可通过最大并发操作数(maxConcurrentOperationCount)来控制并发、串行
         maxConcurrentOperationCount = 1时 只会开启一条线程 任务多时 就是串行执行
         */
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:operation1];//内部已经调用了Start方法
        [queue addOperation:operation2];
        [queue addOperation:operation3];
    }
    
    - (void)task1 {
        NSLog(@"task1  %@",[NSThread currentThread]);
    }
    - (void)task2 {
        NSLog(@"task2  %@",[NSThread currentThread]);
    }
    - (void)task3 {
        NSLog(@"task3  %@",[NSThread currentThread]);
    }

    NSBlockOPeration的使用

    - (void)blockOPerationWithQueue {
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op1 --- %@",[NSThread currentThread]);
        }];
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op2 --- %@",[NSThread currentThread]);
        }];
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op3 --- %@",[NSThread currentThread]);
        }];
        //创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
    }
    //简便方法
     [queue addOperationWithBlock:^{
            NSLog(@"op4 ---- %@",[NSThread currentThread]);
     }];

    自定义NSOperation

    适用于任务比较庞大 代码比较复杂 可把任务抽出来写 有利于代码隐蔽 有利于提高代码的复用性

    #import "LFOperation.h"
    
    @implementation LFOperation
    //告知要执行的任务是什么
    - (void)main {
        NSLog(@"main --- %@",[NSThread currentThread]);
    }
    
    @end
    viewController
    - (void)lfoperation {
        LFOperation *op1 = [[LFOperation alloc] init];
        LFOperation *op2 = [[LFOperation alloc] init];
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:op1];
        [queue addOperation:op2];
    }

     其他用法

    最大并发数

    //控制队列是串行队列还是并发队列
    - (void)test {
        //创建队列
        //默认是并发队列
        self.queue = [[NSOperationQueue alloc] init];
        //设置最大并发数(同一时间最多有多少个操作可以执行) 所以如果最大并发数设置为1 就是串行队列(任务按顺序执行)但是串行执行任务不等于只开启一条线程比如互斥锁
        //如果等于零 不会执行任务 默认值是-1表示不受限制
        self.queue.maxConcurrentOperationCount = 1;
        //封装操作
        NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op1---  %@",[NSThread currentThread]);
        }];
        NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op2---  %@",[NSThread currentThread]);
        }];
        NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op3---  %@",[NSThread currentThread]);
        }];
        NSOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op4---  %@",[NSThread currentThread]);
        }];
        NSOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op5---  %@",[NSThread currentThread]);
        }];
        NSOperation *op6 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op6---  %@",[NSThread currentThread]);
        }];
        
        [self.queue addOperation:op1];
        [self.queue addOperation:op2];
        [self.queue addOperation:op3];
        [self.queue addOperation:op4];
        [self.queue addOperation:op5];
        [self.queue addOperation:op6];
        
    }

    暂停 取消 开始 继续

    - (IBAction)start:(id)sender {
        //创建队列
        //默认是并发队列
        self.queue = [[NSOperationQueue alloc] init];
        self.queue.maxConcurrentOperationCount = 1;
        //封装操作
        NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            for (NSInteger i = 0; i < 10000; i ++) {
                NSLog(@"op1---  %@",[NSThread currentThread]);
            }
            
        }];
        NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            for (NSInteger i = 0; i < 10000; i ++) {
                NSLog(@"op2---  %@",[NSThread currentThread]);
            }
            
        }];
        NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            for (NSInteger i = 0; i < 10000; i ++) {
                NSLog(@"op3---  %@",[NSThread currentThread]);
            }
            
        }];
        [self.queue addOperation:op1];
        [self.queue addOperation:op2];
        [self.queue addOperation:op3];
    }
    
    
    - (IBAction)pause:(id)sender {
        //暂停是可以继续的 但是不能暂停当前正在处于执行状态的任务 要等到当前正在执行状态的任务完成之后才能暂停
        //队列中的任务也是有状态的
        /*
         已经执行的 | 正在执行的 | 处于排队等待状态的
         */
        [self.queue setSuspended:YES];
    }
    
    - (IBAction)goon:(id)sender {
        [self.queue setSuspended:NO];
    }
    
    - (IBAction)cancel:(id)sender {
        //取消 是不可以恢复的 也是把当前任务执行完毕以后 取消后面的任务  内部调用了所有任务的cancel方法
        [self.queue cancelAllOperations];
    }

    但是自定义的写在main 函数里面的操作 即使是多个也会被认为是一个任务 所以暂停 取消 继续没什么效果 但是可以这样判断使取消任务有作用 仅限于取消

    //告知要执行的任务是什么
    - (void)main {
        
        for (NSInteger i = 0; i < 10000; i ++) {
            NSLog(@"op1---  %@",[NSThread currentThread]);
        }
        if (self.isCancelled) {
            return;
        }
        
        for (NSInteger i = 0; i < 10000; i ++) {
            NSLog(@"op2---  %@",[NSThread currentThread]);
        }
        if (self.isCancelled) {
            return;
        }
        for (NSInteger i = 0; i < 10000; i ++) {
            NSLog(@"op3---  %@",[NSThread currentThread]);
        }
    }

    NSOperation 添加操作依赖和监听

    依赖

    - (void)yilai {
        //创建队列
        //默认是并发队列
        self.queue = [[NSOperationQueue alloc] init];
        //封装操作
        NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op1---  %@",[NSThread currentThread]);
        }];
        NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op2---  %@",[NSThread currentThread]);
        }];
        NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op3---  %@",[NSThread currentThread]);
        }];
        NSOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op4---  %@",[NSThread currentThread]);
        }];
        //添加操作依赖
        //注意 不能循环依赖 否则会出现相互等待 相互依赖的两个操作谁都不会执行
        //不同队列中的不同操作也可以设置依赖关系(跨队列依赖)
        [op1 addDependency:op4];//操作一 依赖于操作4  操作4先于操作一执行
        [op4 addDependency:op3];
        [op3 addDependency:op2];
        [self.queue addOperation:op1];
        [self.queue addOperation:op2];
        [self.queue addOperation:op3];
        [self.queue addOperation:op4];
    }

    监听

    - (void)jianting {
        //创建队列
        //默认是并发队列
        self.queue = [[NSOperationQueue alloc] init];
        //封装操作
        NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op1---  %@",[NSThread currentThread]);
        }];
        NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op2---  %@",[NSThread currentThread]);
        }];
        NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op3---  %@",[NSThread currentThread]);
        }];
        NSOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"op4---  %@",[NSThread currentThread]);
        }];
        //添加监听
        op3.completionBlock = ^{
            NSLog(@"op3 已经完成 %@",[NSThread currentThread]);
        };
        
        //添加操作依赖
        //注意 不能循环依赖 否则会出现相互等待 相互依赖的两个操作谁都不会执行
        //不同队列中的不同操作也可以设置依赖关系(跨队列依赖)
        [op1 addDependency:op4];//操作一 依赖于操作4  操作4先于操作一执行
        [op4 addDependency:op3];
        [op3 addDependency:op2];
        [self.queue addOperation:op1];
        [self.queue addOperation:op2];
        [self.queue addOperation:op3];
        [self.queue addOperation:op4];
    

    线程间的通信

    - (void)downLoadImage {
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"&&&&&"]];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            //主线程更新UI
            [NSOperationQueue.mainQueue addOperationWithBlock:^{
                self.imageView.image = image;
            }];
            
        }];
        [queue addOperation:op1];
    }
  • 相关阅读:
    ORACLE触发器具体解释
    秒杀多线程第四篇 一个经典的多线程同步问题
    Java中Integer类的方法
    九大排序算法再总结
    删除条目时的确认对话框
    VirtualBox安装及使用说明和虚拟机安装XP系统图文教程
    J2EE之验证码实现
    教你用笔记本破解无线路由器password
    vSphere HA状况:未知配置错误解决的方法
    HDU 2504 又见GCD
  • 原文地址:https://www.cnblogs.com/huanying2000/p/8531623.html
Copyright © 2020-2023  润新知