NSOperation、NSOperationQueue

NSOperation 和 NSOperationQueue 配合使用也能实现多线程。

NSOperation 继承于 NSObject,是一种抽象类,并不具备封装操作的能力,必须使用它的子类。

使用 NSOperation 子类的三种方式:

1.NSBlockOperation;

2.NSInvocationOperation;

3.自定义 NSOperation,实现内部相应方法。

NSOperation 和 NSOperationQueue实现多线程的步骤:

1)先将需要执行的操作封装到一个 NSOperation(相当于GCD中的任务) 对象中

2)然后将 NSOperation 对象添加到 NSOperationQueue 中

3)系统会自动将 NSOperationQueue 中的 NSOperation取出来

4)将取出的 NSOperation 封装的操作放到一条新线程中执行。

NSInvocationOperation:

#pragma mark - NSInvocationOperation使用1(没什么卵用)
- (void)invocationOp1 {
// 1.创建操作
NSInvocationOperation *invocationOp =\
[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoadImage:) object:@"invocationOp"]; // 2.启动。方法 - (void)start; 表示操作直接在当前线程执行
[invocationOp start];
} #pragma mark - NSInvocationOperation使用2
- (void)invocationOp2 {
// 1.创建操作
NSInvocationOperation *invocationOp =\
[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoadImage:) object:@"invocationOp"]; // 2. 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 3.操作添加到队列,就会异步调度执行这个方法
[queue addOperation:invocationOp];
} #pragma mark - NSInvocationOperation使用3
- (void)invocationOp3 {
// 1. 创建队列( NSOperationQueue 本质上是对GCD中的并发队列的封装)。
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.创建操作并添加到队列
for (int i = ; i < ; i ++) {
// 操作就是 GCD 里异步执行的任务
NSInvocationOperation *invocationOp =\
[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoadImage:) object:@"invocationOp"];
// 3.操作添加到队列,就会异步调度执行这个方法
[queue addOperation:invocationOp];
}
} - (void)downLoadImage:(id)obj {
NSLog(@"NSThread=%@ obj=%@", [NSThread currentThread], obj);
}

总结,NSOperationQueue 就是对 GCD 中队列的封装(主要是并发队列 和 主队列的封装)。而 NsOperation 相当于 GCD 中的任务。

NSBlockOperation

#pragma mark - NSBlockOperation
- (void)blockOp1 {
// 1.创建队列(相当于 GCD 的并发队列)
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.创建多个操作
for (int i = ; i < ; i ++) {
NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"thread=%@ i=%d", [NSThread currentThread], i);
}];
// 把操作放到队列
[queue addOperation:blockOp];
}
} #pragma mark - NSBlockOperation更简单方法
- (void)blockOp2 {
// 1.创建队列(相当于 GCD 的并发队列)
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.创建多个操作
for (int i = ; i < ; i ++) {
[queue addOperationWithBlock:^{
NSLog(@"thread=%@ i=%d", [NSThread currentThread], i);
}];
}
} #pragma mark - NSOperationQueue相当于 GCD中的并发队列。但是也可以获取主线程和当前队列
- (void)operationQueue {
// 1.创建队列(相当于 GCD 的并发队列)
NSOperationQueue *queue = [NSOperationQueue mainQueue]; // 2.创建多个操作
for (int i = ; i < ; i ++) {
// 操作就是 GCD 里异步执行的任务,并不会马上就执行
NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"thread=%@ i=%d", [NSThread currentThread], i);
}];
// 把操作放到队列
[queue addOperation:blockOp];
}
NSLog(@"完成·········");
}
打印结果:
-- ::20.723 NSOperation[:] 完成·········
-- ::20.724 NSOperation[:] thread=<NSThread: 0x7fa781703410>{number = , name = main} i=
-- ::20.724 NSOperation[:] thread=<NSThread: 0x7fa781703410>{number = , name = main} i=
-- ::20.725 NSOperation[:] thread=<NSThread: 0x7fa781703410>{number = , name = main} i=
-- ::20.725 NSOperation[:] thread=<NSThread: 0x7fa781703410>{number = , name = main} i=
-- ::20.725 NSOperation[:] thread=<NSThread: 0x7fa781703410>{number = , name = main} i=
-- ::20.725 NSOperation[:] thread=<NSThread: 0x7fa781703410>{number = , name = main} i=

NSOperation:

- (void)invocation_block_Op {
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; for (int i = ; i < ; i ++) {
// 不创建操作,使用 - (void)addOperationWithBlock:(void (^)(void))block; 直接添加操作到队列
[queue addOperationWithBlock:^{
NSLog(@"耗时操作thread=%@ i=%d", [NSThread currentThread], i);
}];
} // 操作
NSInvocationOperation *inOp = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoadImage:) object:@"组团使用"];
[queue addOperation:inOp]; // block操作
NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOperationWithBlock - thread=%@", [NSThread currentThread]);
}];
// - (void)addExecutionBlock:(void (^)(void))block; 添加的操作,和上面是同级别的操作
[blockOp addExecutionBlock:^{
NSLog(@"addExecutionBlock - thread=%@", [NSThread currentThread]);
}];
[queue addOperation:blockOp];
}
打印结果:
-- ::37.854 NSOperation[:] thread=<NSThread: 0x7fdf0c801b40>{number = , name = (null)} i=
-- ::37.854 NSOperation[:] thread=<NSThread: 0x7fdf0a4311d0>{number = , name = (null)} i=
-- ::37.854 NSOperation[:] thread=<NSThread: 0x7fdf0a7f9950>{number = , name = (null)} i=
-- ::37.854 NSOperation[:] thread=<NSThread: 0x7fdf0a60c940>{number = , name = (null)} i=
-- ::37.854 NSOperation[:] thread=<NSThread: 0x7fdf0c801b40>{number = , name = (null)} i=
-- ::37.854 NSOperation[:] thread=<NSThread: 0x7fdf0a4311d0>{number = , name = (null)} i=
-- ::37.855 NSOperation[:] NSThread=<NSThread: 0x7fdf0c802ba0>{number = , name = (null)} obj=组团使用
-- ::37.855 NSOperation[:] blockOperationWithBlock - thread=<NSThread: 0x7fdf0a7f9950>{number = , name = (null)}
-- ::37.855 NSOperation[:] addExecutionBlock - thread=<NSThread: 0x7fdf0c801b40>{number = , name = (null)}

线程间通信:

#pragma mark - NSOperation 线程间通信
- (void)communicationBetweenThreads {
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 添加操作,异步执行
[queue addOperationWithBlock:^{
// 耗时操作
// code····
NSLog(@"耗时操作 : %@", [NSThread currentThread]); // 主线程刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// code····
NSLog(@"刷新UI : %@", [NSThread currentThread]);
}];
}];
} #pragma mark - GCD 实现线程通讯
- (void)GCD_threads {
// 全局并发队列异步执行
dispatch_async(dispatch_get_global_queue(, ), ^{
// 耗时操作
// code····
NSLog(@"耗时操作 : %@", [NSThread currentThread]); // 主线程刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
// code····
NSLog(@"刷新UI : %@", [NSThread currentThread]);
});
});
}
打印结果:
-- ::55.421 NSOperation[:] 耗时操作 : <NSThread: 0x7fd72866a9c0>{number = , name = (null)}
-- ::55.421 NSOperation[:] 耗时操作 : <NSThread: 0x7fd7287aaf50>{number = , name = (null)}
-- ::55.422 NSOperation[:] 刷新UI : <NSThread: 0x7fd728704dc0>{number = , name = main}
-- ::55.422 NSOperation[:] 刷新UI : <NSThread: 0x7fd728704dc0>{number = , name = main}

最大并发数:

同时执行的操作的数量。

#pragma mark - 最大并发数
- (void)max_concurrent {
// 设置最大并发数,是同时执行的操作的数量
self.queue.maxConcurrentOperationCount = 2;
for (int i = 0; i < 10; i ++) {
[self.queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"%@", [NSThread currentThread]);
}];
}
}
打印结果:
2016-04-06 12:54:09.065 NSOperation[2673:349657] <NSThread: 0x7fd5d8c70970>{number = 3, name = (null)}
2016-04-06 12:54:09.065 NSOperation[2673:349573] <NSThread: 0x7fd5d8c8d700>{number = 2, name = (null)}
2016-04-06 12:54:11.071 NSOperation[2673:349568] <NSThread: 0x7fd5d8d361c0>{number = 4, name = (null)}
2016-04-06 12:54:11.071 NSOperation[2673:349657] <NSThread: 0x7fd5d8c70970>{number = 3, name = (null)}
2016-04-06 12:54:13.073 NSOperation[2673:349657] <NSThread: 0x7fd5d8c70970>{number = 3, name = (null)}
2016-04-06 12:54:13.073 NSOperation[2673:349568] <NSThread: 0x7fd5d8d361c0>{number = 4, name = (null)}
2016-04-06 12:54:15.078 NSOperation[2673:349573] <NSThread: 0x7fd5d8c8d700>{number = 2, name = (null)}
2016-04-06 12:54:15.078 NSOperation[2673:349568] <NSThread: 0x7fd5d8d361c0>{number = 4, name = (null)}
2016-04-06 12:54:17.081 NSOperation[2673:349657] <NSThread: 0x7fd5d8c70970>{number = 3, name = (null)}
2016-04-06 12:54:17.081 NSOperation[2673:349568] <NSThread: 0x7fd5d8d361c0>{number = 4, name = (null)}
分析:最大并发数是同时执行的操作的数量。以上代码,我们实现的是每两秒执行一次(查看打印的时间),每次执行两个操作。之所以所有相同的线程是因为:任务执行完毕之后,线程会被回收到线程池,然后再拿出来使用的过程!

挂起/恢复:

#pragma mark - 取消队列里面所有操作(已经执行的操作不会取消)
// 取消操作,并不影响队列的挂起状态
- (IBAction)cancelAllOpeation:(id)sender { // 取消队列里面所有操作(队列里的操作都被移除)
[self.queue cancelAllOperations];
NSLog(@"取消队列里面所有操作"); /**
场景:用户点击下载视频A、B、C、D;
然后点击 挂起队列(暂停);
再然后,点击取消所有操作;
最后,再次点击下载视频 -----> 导致无法下载,原因是 : 所有操作已取消。(点击挂起/恢复按钮可以实现下载) 添加如下代码,解决这个BUG
*/
// 取消队列挂起状态(只要取消了队列里的操作,我们就取消队列的挂起状态,以便于后续的开始)
self.queue.suspended = NO;
} #pragma mark - 挂起/继续(挂起是对队列的挂起,挂起之后,队列不再添加操作到队列去执行;但是挂起不会影响已经执行的操作)
- (IBAction)pause:(id)sender { // 判断当前队列中是否有操作(防止用户先点击了“暂停/继续按钮”,导致队列被挂起之后,用户无法执行后续的操作);
if (self.queue.operationCount == 0) {
NSLog(@"没有操作!!");
return;
} self.queue.suspended = !self.queue.isSuspended;
if (self.queue.isSuspended) {
NSLog(@"暂停");
} else {
NSLog(@"继续");
}
}
结论:
@property (getter=isSuspended) BOOL suspended; // 挂起/恢复队列
@property (readonly) NSUInteger operationCount; // 队列里面的操作数
- (void)cancelAllOperations; // 取消所有操作(移除掉了)
可以配合使用,来实现需求。

设置依赖:

/**
* @brief 添加数组操作
*
* @param ops 数组操作
* @param wait 是否等待.YES : 等待前面的操作执行完毕再执行该方法后面的操作。 NO : 不等待前面的操作执行完毕就执行后面的操作。
*/
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;
#pragma mark - 设置依赖 Methods
- (void)dependency {
NSBlockOperation *blockOp1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载A:%@", [NSThread currentThread]);
}];
NSBlockOperation *blockOp2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载B:%@", [NSThread currentThread]);
}];
NSBlockOperation *blockOp3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"刷新UI:%@", [NSThread currentThread]);
}]; // 指定任务之间的依赖关系,依赖关系可以跨队列(子线程下载,主线程刷新UI)(不要出现循环依赖)
[blockOp2 addDependency:blockOp1];
[blockOp3 addDependency:blockOp2]; // 并发队列中添加操作
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[blockOp1, blockOp2] waitUntilFinished:YES]; // 主队列刷新UI
[[NSOperationQueue mainQueue] addOperation:blockOp3]; NSLog(@"执行完毕····");
}

参考文档:

NSOperation官方文档

NSOperationQueue官方文档

NSOperation、NSOperationQueue的更多相关文章

  1. iOS 多线程 NSOperation、NSOperationQueue

    1. NSOperation.NSOperationQueue 简介 NSOperation.NSOperationQueue 是苹果提供给我们的一套多线程解决方案.实际上 NSOperation.N ...

  2. NSOperation、NSOperationQueue(III)

    NSOperation.NSOperationQueue 常用属性和方法归纳 NSOperation 常用属性和方法 a. 取消操作方法 //可取消操作,实质是标记 isCancelled 状态. - ...

  3. NSOperation、NSOperationQueue(II)

    NSOperationQueue 控制串行执行.并发执行 NSOperationQueue 创建的自定义队列同时具有串行.并发功能 这里有个关键属性 maxConcurrentOperationCou ...

  4. 『NSOperation、NSOperationQueue』详解

    1. NSOperation.NSOperationQueue 简介 NSOperation.NSOperationQueue 是苹果提供给我们的一套多线程解决方案.实际上 NSOperation.N ...

  5. iOS自学之NSOperation、NSOperationQueue、Background

    iOS中多线程编程主要分为NSThread.NSOperation和GCD,今天主要记录下自己在学习NSOperation中的点滴-如有不对的地方帮忙指出下,PS:人生第一次写blog,各位看官请轻虐 ...

  6. iOS-----使用NSOperation与NSOperationQueue实现多线程

    使用NSOperation与NSOperationQueue实现多线程 NSOperation与NSOperationQueue的基本理论如下. NSOperationQueue 代表一个FIFO的队 ...

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

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

  8. GCD的同步异步串行并行、NSOperation和NSOperationQueue一级用dispatch_once实现单例

    转:http://www.tuicool.com/articles/NVVnMn (1)GCD实现的同步异步.串行并行. ——同步sync应用场景:用户登录,利用阻塞 ——串行异步应用场景:下载等耗时 ...

  9. 【iOS开发-91】GCD的同步异步串行并行、NSOperation和NSOperationQueue一级用dispatch_once实现单例

    (1)GCD实现的同步异步.串行并行. --同步sync应用场景:用户登录,利用堵塞 --串行异步应用场景:下载等耗时间的任务 /** * 由于是异步.所以开通了子线程.可是由于是串行队列,所以仅仅须 ...

随机推荐

  1. 8.Python初窥门径(文件操作)

    Python (文件操作) 一.文件操作方式 打开文件 open 操作文件 read or write 关闭文件 close 二.打开文件的方式(第一种) 语法 : f=open("文件&q ...

  2. 深入V8引擎-Time模块介绍

    积跬步,行千里,先从最简单的开始写. 这一篇介绍V8中的时间模块,与libuv粗糙的update_loop_time方法不同,V8有一套独立完整的类负责管理时间. 该类位于src/base/platf ...

  3. 如何删除.DS_Store文件?

    .DS_Store出现在Desktop和其它地方,看它碍眼,它是什么,详见百度百科 http://baike.baidu.com/link?url=yLTDHR6OS66-981wpCY-mWPF7a ...

  4. Java流程控制和数组

    流程控制 Java中三种基本的流程控制结构:顺序结构,分支结构和循环结构. 顺序结构,任何编程语言中都会有的程序结构. 分支结构:Java语言中常见的两种, if语句和switch语句. if语句,使 ...

  5. SQL SERVER CAST 和 CONVERT 函数

    遇到CAST 函数转化数字不一致情况, select CAST('0000000011237590798' AS money) / 100 AS Amount--output : 112375907. ...

  6. The database could not be exclusively locked to perform the operation(SQL Server 5030错误解决办法)(转)

    Microsoft SQL Server 5030错误解决办法 今天在使用SQL Server时,由于之前创建数据库忘记了设置Collocation,数据库中插入中文字符都是乱码,于是到DataBas ...

  7. 集合中的 for-Each循环

     数组的加强型的for-Each循环很简单,我们再来看一下集合中的for-Each 循环又是怎么样的.我们都知道集合中的遍历都是通过迭代(iterator)完成的.也许有人说,也可以按照下面的方式来遍 ...

  8. [LOJ 2082] 「JSOI2016」炸弹攻击 2

    [LOJ 2082] 「JSOI2016」炸弹攻击 2 链接 链接 题解 枚举发射源,将发射源当做原点,对敌人和激光塔极角排序. 由于敌人纵坐标均为正,而其它点均为负,因此每两个角度差在 \(\pi\ ...

  9. Codeforces Round #561 (Div. 2) C. A Tale of Two Lands

    链接:https://codeforces.com/contest/1166/problem/C 题意: The legend of the foundation of Vectorland talk ...

  10. 关于FutureTask的探索

    之前关于Java线程的时候,都是通过实现Runnable接口或者是实现Callable接口,前者交给Thread去run,后者submit到一个ExecutorService去执行. 然后知道了还有个 ...