1.简介

NS(基于OC语言)是对GCD(基于C语言)的封装,让开发者能够更加友好的方便的去使用多线程技术。

2.NSOperation的基本使用

NSOperation是抽象类,所以如果要使用NSOperation则需要继承和实现它。

内置的子类常用的有两个:

  • NSInvocationOperation
  • NSBlockOperation

使用步骤:

  无论上述哪一个一般使用都遵循以下步骤:

  1. 创建队列/获取主队列
  2. 创建子类实例
  3. 加入队列

例子:https://github.com/xufeng79x/NSOperation

3.NSOperation再研究

NSOperation 中有这么一句话:

An operation object is a single-shot object—that is, it executes its task once and cannot be used to execute it again.

所以说一个操作只能被执行一次,当如下代码将某个操作加到多个队列的时候会出现错误

[queue1 addOperation:op1];

[queue2 addOperation:op1];

错误:

-- :::] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperation:]: operation is already enqueued on a queue'

4.线程间通信

和GCD一样,当在某个线程中获取到信息需要更新UI的时候,那么更新UI需要在主线程中进行,所以这个就涉及到线程间的通信。

// MARK: 线程间通信

- (void) test_threads_com
{
    //1. 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    //2. 在其他形成做耗时操作
    [queue addOperationWithBlock:^{
        NSLog(@"耗时操作---------%@", [NSThread currentThread]);

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"更新UI---------%@", [NSThread currentThread]);
        }];
    }];
}

上述代码通过队列的不同从而让人物去在不同线程中执行,main队列中的人物都是在主线程中执行的。

5.高级操作

介绍最大并发数,挂起,取消全部线程

注意:以上都是在队列上操作的,只对队列中未运行的线程其作用,而对正在运行和已经运行完毕的线程无效。

最大并发数:

// MARK:最大并发数
- (void)test_MaxThreadsNum
{
    //1. 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    //2. 设定队列上的最大并发数
    [queue setMaxConcurrentOperationCount:];

    ; i < ; i++) {
        //3. 直接将operation放到queue中
        [queue addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:5.0f];
            NSLog(@"------------%@-------%d", [NSThread currentThread], i);
        }];
    }
}

输出:

可以看到每一次只有两个线程运行。

挂起:

// MARK:挂起
// 测试步骤:新增全局队列,运行的时候点击暂停/开始按钮来让队列挂起或者取消挂起
- (void)test_suspend
{
    // 设定最大并发数,便于观察
    [self.globQueue setMaxConcurrentOperationCount:];
    ; i < ; i++) {
        //1直接将operation放到queue中
        [self.globQueue addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:5.0f];
            NSLog(@"------------%@-------%d", [NSThread currentThread], i);
        }];
    }
}

测试结果:

当程序运行的时候点击【暂定】后程序不再运行队列中的操作,当我们再次点击【开始】后继续运行队列中剩余的操作。

如前所述,挂起操作也是对于队列中的操作,而对已经运行的操作则不影响,所以我们看到暂停日志后又有两个任务打印出了结束日志。

另外一个细节就是当点击暂定时候队列中显示还有16个,再次点击开始的时候发现队列中还有14个,这说明只有当操作运行完毕后才会从队列中移除。

取消全部:

// MARK:取消全部
// 测试步骤:点击取消全部按钮
- (void)test_cancelALl
{
    // 设定最大并发数,便于观察
    [self.globQueue setMaxConcurrentOperationCount:];
    ; i < ; i++) {
        //1直接将operation放到queue中
        [self.globQueue addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:5.0f];
            NSLog(@"------------%@-------%d", [NSThread currentThread], i);
        }];
    }
}

测试结果:

-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] the queue has been cancelall!  in queue now!
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] the queue has been cancelall!  in queue now!

可以看到当运行的时候点击取消后后续所有任务都不会再运行。

值得注意的是点击取消后,还有运行日志打出,还是和之前所述一样,取消全部不会影响正在运行的操作。

另外需要注意的是,当点击取消的时候,日志还显示还有18个任务,当正在运行的操作完毕后,再次点击取消则显示为0个。

说明只有运行的任务运行完毕后才会清空队列。

思考--队列中的任务会按序执行吗?

// MARK:是否按序执行
- (void)test_executeOrder
{
    // 设定最大并发数为1,便于观察
    [self.globQueue setMaxConcurrentOperationCount:];
    ; i < ; i++) {
        //1直接将operation放到queue中
        [self.globQueue addOperationWithBlock:^{
            //[NSThread sleepForTimeInterval:1.0f];
            NSLog(@"------------%@-------%d", [NSThread currentThread], i);
        }];
    }
}

日志:

-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (
-- :::] ------------<NSThread: , name = (

经过多次测试,发现是先进先出的有序执行。

 6.操作依赖

-线性依赖:

在NSOperation中可以进行操作的依赖设置,一个操作依赖于另外一个操作,可以在不同队列间的操作间设置依赖。

// MARK:操作间依赖
- (void)test_dependsLine
{
    //1. 建立第一个队列
    NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
    NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op1 in queue1------%@", [NSThread currentThread]);
    }];

    //2. 建立第二个队列
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op2 in queue2------%@", [NSThread currentThread]);
    }];

    //3. 建立第二个队列
    NSOperationQueue *queue3 = [[NSOperationQueue alloc] init];
    NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op3 in queue3------%@", [NSThread currentThread]);
    }];

    // 设定依赖关系
    [op3 addDependency:op2];
    [op2 addDependency:op1];

    // 一旦放入队列则将执行
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
    [queue3 addOperation:op3];
}

结果:

-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}

可以看到即使在不同的队列中操作依然按照设定的顺序执行。

-树形依赖

// MARK:操作间依赖-树形依赖
- (void)test_dependstree
{
    //1. 建立第一个队列
    NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
    NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op1 in queue1------%@", [NSThread currentThread]);
    }];

    //2. 建立第二个队列
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op2 in queue2------%@", [NSThread currentThread]);
    }];

    //3. 建立第二个队列
    NSOperationQueue *queue3 = [[NSOperationQueue alloc] init];
    NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op3 in queue3------%@", [NSThread currentThread]);
    }];

    // 设定依赖关系
    [op3 addDependency:op2];
    [op3 addDependency:op1];

    // 一旦放入队列则将执行
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
    [queue3 addOperation:op3];
}

结果:

-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}

可以看到op3需要等待op1和op2执行完毕后才会去执行。

-思考:可以先放队列在设定依赖吗?

// MARK:操作间依赖-先放队列后设定依赖
- (void)test_dependAfterQueued
{
    //1. 建立第一个队列
    NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
    NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op1 in queue1------%@", [NSThread currentThread]);
    }];

    //2. 建立第二个队列
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op2 in queue2------%@", [NSThread currentThread]);
    }];

    //3. 建立第二个队列
    NSOperationQueue *queue3 = [[NSOperationQueue alloc] init];
    NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op3 in queue3------%@", [NSThread currentThread]);
    }];

    // 一旦放入队列则将执行
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
    [queue3 addOperation:op3];

    // 设定依赖关系
    [op3 addDependency:op2];
    [op3 addDependency:op1];
}

结果:

-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op2 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op1 , name = (null)}
-- :::] ------I am op3 , name = (null)}
-- :::] ------I am op2 , name = (null)}

可以看到并没有按照依赖关系的顺序执行,所以说关系必须在操作放入队列之前需要提前设定。

[New learn] NSOperation基本使用的更多相关文章

  1. iOS多线程之9.自定义NSOperation

      本文主要讲如何自定义NSOperation,以及自定义NSOperation的一些注意事项,以下载图片为例. 新建一个类,继承于NSOperation. CustomOperation.h 代码 ...

  2. iOS多线程之8.NSOPeration的其他用法

      本文主要对NSOPeration的一些重点属性和方法做出介绍,以便大家可以更好的使用NSOPeration. 1.添加依赖 - (void)addDependency:(NSOperation * ...

  3. iOS多线程之7.NSOperation的初识

    NSOperation和GCD一样,不用我们管理线程的生命周期,加锁等问题,只要把操作封装进NSOperation中,系统会自动帮我们创建线程,执行操作.而且他是面向对象的,我们看起来更容易理解,使用 ...

  4. 4.4 多线程进阶篇<下>(NSOperation)

    本文并非最终版本,如有更新或更正会第一时间置顶,联系方式详见文末 如果觉得本文内容过长,请前往本人"简书" 本文源码 Demo 详见 Github https://github.c ...

  5. Atitit learn by need 需要的时候学与预先学习知识图谱路线图

    Atitit learn by need 需要的时候学与预先学习知识图谱路线图 1. 体系化是什么 架构 知识图谱路线图思维导图的重要性11.1. 体系就是架构21.2. 只见树木不见森林21.3. ...

  6. 认识和使用NSOperation

    原文链接:http://www.jianshu.com/p/2de9c776f226 NSOperation是OC中多线程技术的一种,是对GCD的OC包装.它包含队列(NSOperationQueue ...

  7. 多线程下NSOperation、NSBlockOperation、NSInvocationOperation、NSOperationQueue的使用

    本篇文章主要介绍下多线程下NSOperation.NSBlockOperation.NSInvocationOperation.NSOperationQueue的使用,列举几个简单的例子. 默认情况下 ...

  8. iOS NSOperation 封装 通知实现界面更新

    #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface MYOperation : NSOpe ...

  9. iOS NSOperation 异步加载图片 封装NSOperation 代理更新

    #import <Foundation/Foundation.h> @class MYOperation; @protocol MYOperationDelecate <NSObje ...

随机推荐

  1. hadoop 把mapreduce任务从本地提交到hadoop集群上运行

    MapReduce任务有三种运行方式: 1.windows(linux)本地调试运行,需要本地hadoop环境支持 2.本地编译成jar包,手动发送到hadoop集群上用hadoop jar或者yar ...

  2. 【POJ2728】Desert King(分数规划)

    [POJ2728]Desert King(分数规划) 题面 vjudge 翻译: 有\(n\)个点,每个点有一个坐标和高度 两点之间的费用是高度之差的绝对值 两点之间的距离就是欧几里得距离 求一棵生成 ...

  3. BZOJ4355:Play with sequence——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4355 维护一个长度为N的序列a,现在有三种操作: 1)给出参数U,V,C,将a[U],a[U+1] ...

  4. 洛谷 P2446 [SDOI2010]大陆争霸 解题报告

    P2446 [SDOI2010]大陆争霸 题目背景 在一个遥远的世界里有两个国家:位于大陆西端的杰森国和位于大陆东端的克里斯国.两个国家的人民分别信仰两个对立的神:杰森国信仰象征黑暗和毁灭的神曾·布拉 ...

  5. 【逆序对相关/数学】【P1966】【NOIP2013D1T2】 火柴排队

    传送门 Description 涵涵有两盒火柴,每盒装有 $n$ 根火柴,每根火柴都有一个高度. 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为:$ \sum ...

  6. 利用caffe的solverstate断点训练

    你可以从系统 /tmp 文件夹获取,名字是什么 caffe.ubuntu.username.log.INFO.....之类 ====================================== ...

  7. HBase客户端访问超时的多个因素及参数

    在一个需要低延时响应的hbase集群中,使用hbase默认的客户端超时配置简直就是灾难. 但是我们可以考虑在客户端上加上如下几个参数,去改变这种状况: 1. hbase.rpc.timeout: RP ...

  8. [技巧篇]12.从Spring的编码过滤器说起

    有一枚学生问问了我一个问题,突然灵感爆发,他使用的Spring的过滤器,前台利用GET方式向后端发出一个请求,由于里面含有中文数据,结果在后端显示的是乱码,他问我为什么?明明在Spring里面也配了字 ...

  9. POJ 3087 Shuffle'm Up DFS

    link:http://poj.org/problem?id=3087 题意:给你两串字串(必定偶数长),按照扑克牌那样的洗法(每次从S2堆底中拿第一张,再从S1堆底拿一张放在上面),洗好后的一堆可以 ...

  10. [Luogu 2221] HAOI2012 高速公路

    [Luogu 2221] HAOI2012 高速公路 比较容易看出的线段树题目. 由于等概率,期望便转化为 子集元素和/子集个数. 每一段l..r中,子集元素和为: \(\sum w_{i}(i-l+ ...