[New learn] NSOperation基本使用
1.简介
NS(基于OC语言)是对GCD(基于C语言)的封装,让开发者能够更加友好的方便的去使用多线程技术。
2.NSOperation的基本使用
NSOperation是抽象类,所以如果要使用NSOperation则需要继承和实现它。
内置的子类常用的有两个:
- NSInvocationOperation
- NSBlockOperation
使用步骤:
无论上述哪一个一般使用都遵循以下步骤:
- 创建队列/获取主队列
- 创建子类实例
- 加入队列
例子: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基本使用的更多相关文章
- iOS多线程之9.自定义NSOperation
本文主要讲如何自定义NSOperation,以及自定义NSOperation的一些注意事项,以下载图片为例. 新建一个类,继承于NSOperation. CustomOperation.h 代码 ...
- iOS多线程之8.NSOPeration的其他用法
本文主要对NSOPeration的一些重点属性和方法做出介绍,以便大家可以更好的使用NSOPeration. 1.添加依赖 - (void)addDependency:(NSOperation * ...
- iOS多线程之7.NSOperation的初识
NSOperation和GCD一样,不用我们管理线程的生命周期,加锁等问题,只要把操作封装进NSOperation中,系统会自动帮我们创建线程,执行操作.而且他是面向对象的,我们看起来更容易理解,使用 ...
- 4.4 多线程进阶篇<下>(NSOperation)
本文并非最终版本,如有更新或更正会第一时间置顶,联系方式详见文末 如果觉得本文内容过长,请前往本人"简书" 本文源码 Demo 详见 Github https://github.c ...
- Atitit learn by need 需要的时候学与预先学习知识图谱路线图
Atitit learn by need 需要的时候学与预先学习知识图谱路线图 1. 体系化是什么 架构 知识图谱路线图思维导图的重要性11.1. 体系就是架构21.2. 只见树木不见森林21.3. ...
- 认识和使用NSOperation
原文链接:http://www.jianshu.com/p/2de9c776f226 NSOperation是OC中多线程技术的一种,是对GCD的OC包装.它包含队列(NSOperationQueue ...
- 多线程下NSOperation、NSBlockOperation、NSInvocationOperation、NSOperationQueue的使用
本篇文章主要介绍下多线程下NSOperation.NSBlockOperation.NSInvocationOperation.NSOperationQueue的使用,列举几个简单的例子. 默认情况下 ...
- iOS NSOperation 封装 通知实现界面更新
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface MYOperation : NSOpe ...
- iOS NSOperation 异步加载图片 封装NSOperation 代理更新
#import <Foundation/Foundation.h> @class MYOperation; @protocol MYOperationDelecate <NSObje ...
随机推荐
- 51nod 1286 三段子串(树状数组+拓展kmp)
题意: 给定一个字符串S,找到另外一个字符串T,T既是S的前缀,也是S的后缀,并且在中间某个地方也出现一次,并且这三次出现不重合.求T最长的长度. 例如:S = "abababababa&q ...
- BZOJ1568:[JSOI2008]Blue Mary开公司——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=1568 李超线段树(不会的话去网上搜吧……). 完. #include<map> #in ...
- 在linux服务器上搭建相对安全的FTP服务器
一.如何在Linux服务器上安装vsftp不在多说,直接介绍如何进行安全性配置: 二.编辑vsftp.conf文件 关键配置项如下: anonymous_enable=NO /禁止匿名用户登录 loc ...
- 第一次BC
BestCoder Round #90 1001 Kblack loves flag 太弱只写了这一道水题. 首先这个题面就是,完全不知道它在说什么.开始5mins后我还完全不知道这个题想要表达什么. ...
- 【状压DP】【UVA11825】 Hackers' Crackdown
传送门 Description 你是一个hacker,侵入了一个有着n台计算机(编号为1.2.3....n)的网络.一共有n种服务,每台计算机都运行着所有服务.对于每台计算机,你都可以选择一项服务,终 ...
- Codeforces Round #332 (Div. 2)B. Spongebob and Joke
B. Spongebob and Joke time limit per test 2 seconds memory limit per test 256 megabytes input standa ...
- VC对话框实现添加滚动条实现滚动效果
对话框滚动条及滚动效果实现,用的api主要有: ScrollWindow, SetScrollInfo, GetScrollInfo, SetWindowOrgEx.涉及的数据结构为SCROLLINF ...
- JS this的指向
总结:this指向调用函数的那个对象. 在不同的应用场景this的指向有所不同,但细细思考都符合总结的意思. 场景一:一般的函数调用 这种常见的函数调用方式this指向的是window,因为相当于是w ...
- ACM1880魔咒词典
魔咒词典 Problem Description 哈利波特在魔法学校的必修课之一就是学习魔咒.据说魔法世界有100000种不同的魔咒,哈利很难全部记住,但是为了对抗强敌,他必须在危急时刻能够调用任何一 ...
- centOS升级部分功能后,不能进入桌面(桌面)
在Linux中安装nginx,安装过程中需要安装c++等一部分环境,装完后,莫名奇妙的centos就不能进入桌面了,命令窗口可以进去. 网上查了查资料,说是升级了一部分功能,和内核有冲突.需要更新下系 ...