NSOperation 开发
目录
- 1.简介
- 2.Operation对象
- 3.自定义Operation对象
- 4.通过Operation Queues运行Operation对象
- 5.手动运行Operation对象
一、简介
Cocoa提供一个NSOperation对象用于执行一些异步的任务,NSOperation只是承载任务的,只能通过把operation添加到operation queue运行或者手动开线程运行。NSOperation与GCD相似,都是Cocoa提供的多线程编程工具,但是NSOperation提交的任务如果还没有执行时可以取消的,而GCD的任务提交后就完全不受我们控制了。
二、Operation对象
NSOperation 其实是一个虚类(它并非没有任何实现,相反它已经实现了一些常用的功能,只是cocoa限定了它不能直接实例化),只有继承实现后才能使用。cocoa实现了NSOperation的两个子类,分别是NSBlockOperation、NSInvocationOperation,这两个类可以用于执行一般的多线程任务。
NSBlockOperation类的作用主要是管理block执行,可以是一个或者多个block。
NSInvocationOperation类则是管理selector执行,这个只能是一个selector。
我们只讲NSBlockOperation类,先看一个简单例子:
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSBlockOperation * operationA = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation block A.");
}];
[queue addOperation:operationA]; // 输出
// operation block A.
可以看到创建一个NSBlockOperation任务并运行很简单。只要创建一个NSBlockOperation类出入一个任务block,再添加到一个NSOperationQueue(下文会讲到)对象就能运行了。
学完这个例子我们再来学习NSBlockOperation的有哪些行为。
NSBlockOperation是支持添加多个block任务,被添加的这些block任务是并行运行的。NSBlockOperation对象还可以设置一个complete block,在所有的block任务都执行完后就会执行这个complete block。看代码
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSBlockOperation * operationA = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation block A(1).");
}];
// 添加
[operationA addExecutionBlock:^{
NSLog(@"operation block A(2).");
}];
// 结束block
[operationA setCompletionBlock:^{
NSLog(@"operation block A compelete.");
}];
[queue addOperation:operationA]; //输出
2017-01-10 16:26:30.306 IOSTest[74701:3177097] operation block A(1).
2017-01-10 16:26:30.306 IOSTest[74701:3177217] operation block A(2).
2017-01-10 16:26:30.308 IOSTest[74701:3177217] operation block A compelete.
NSBlockOperation有个name属性,可以通过setName来设置,通过getName来获取。
NSBlockOperation本身是有优先级的,当被添加到operation queue时,高优先级的operation优先执行。operation 对象可以通过setQueuePriority函数来设置优先级。
我们还可以调用operation 对象的waitUntilFinished函数,挂起当前线程直到operation对象的任务都执行完成才返回。
operation对象如果还没执行,你可以通过调用cancel函数取消operation的运行。
operation对象与operation对象之间可以有依赖的关系。比如operation A依赖 operation B和 operation C,当准备执行A是会去获取B、C的状态,如果B C没有执行完成的话是不会执行的A的,只有到B C,执行完成后,才回去执行A。依赖使得你可以在使不同的operation对象可以串行运行。
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSBlockOperation * operationA = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation block A.");
}];
NSBlockOperation * operationB = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:];
NSLog(@"operation block B.");
}];
NSBlockOperation * operationC = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation block C.");
}];
// 依赖
[operationA addDependency:operationB];
[operationA addDependency:operationC]; [queue addOperation:operationA];
[queue addOperation:operationB];
[queue addOperation:operationC];
//输出
2017-01-10 16:41:48.670 IOSTest[74726:3188994] operation block C.
2017-01-10 16:41:49.670 IOSTest[74726:3188761] operation block B.
2017-01-10 16:41:49.671 IOSTest[74726:3188761] operation block A
operation对象的所有属性都支持KVO,可以用KVO来监听operation对象的属性变化及时掌握operation对象的状态变化,其中有这些属性:
- ready - 正在准备
- cancelled - 是否取消
- executing - 正在执行
- finished - 是否完成
- asynchronous - 是否异步
- name - 名字
其中由 ready,executing,finished,cancelled这四个属性标示了operation对象的生命周期,也正是这几个属性使得我们的operation对象的任务能够被取消、依赖,我们将会在下一节讲到。
三、自定义Operation对象
自定义一个operation对象之前你应该把一件事确定下来,那就是你的operation对象实现的操作是同步还异步。
1.如果是实现同步操作的话,自定义operation类所需要做的事情就简单一点,只要覆盖main函数既可,其他的行为、特性由其的父类NSOperation来提供。具体看代码。
// NonConcurrentOperation.h
// IOSTest
//
// Created by 朱国清 on 17/1/11.
// Copyright © 2017年 朱国清. All rights reserved.
// #import <Foundation/Foundation.h>
@interface NonConcurrentOperation : NSOperation
-(instancetype)initWithCalcCount:(NSInteger)calcCount;
@end
// NonConcurrentOperation.m
// IOSTest
//
// Created by 朱国清 on 17/1/11.
// Copyright © 2017年 朱国清. All rights reserved.
//
#import "NonConcurrentOperation.h"
@interface NonConcurrentOperation()
@property (nonatomic)NSInteger calcCount;
@end
// 同步的任务,只要实现main方法既可,其他状态由NSOperation来控制
@implementation NonConcurrentOperation
-(instancetype)initWithCalcCount:(NSInteger)calcCount{
if (self = [super init]) {
self.calcCount = calcCount;
}
return self;
}
// 由start()调用 start是入口,主要作用是更新status和调用main
// 当然调用main前也会先判断一下status
/*
void start(){
// 判断 isCancel 如果已经被cancel了就直接设置isFinished为true,不调用main函数
// 执行mian
[self willChangeValueForKey:@"isFinished"];
main();
self.isFinished = true;
[self didChangeValueForKey:@"isFinished"];
}
*/
-(void)main{
while (!self.cancelled && self.calcCount-- > ) {
[NSThread sleepForTimeInterval:];
NSLog(@"calc[%d]",(int)self.calcCount);
}
}
@end
NonConcurrentOperation类实现了两个方法,一个是initWithCalcCount函数,这个函数传入一个代表计算任务个数的参数,并返回一个实例。可以根据你个人的需要传入不同的数据。
另一个是main函数,这个函数用来编写你任务代码的,它是由start函数调用的。一个operation对象被添加queue后,当这个operation可以运行时(queue的线程可用/依赖都执行完成),queue就会调用operation的start方法来启动这个operation对象。
自定义main函数时有一点值得的注意的是,在运行你的具体任务代码前应该判断一下cancelled 属性是否为真,如果为真的话就直接return 结束main函数的运行,为假是才往下执行。
我们可以大概猜测start函数是怎么运作的,上述代码已经写出start函数的大概。相信大家都能看到懂。
2.如果自定义的是并行任务的operation类的话,就稍微复杂一点点,得自己维护一些状态属性,并且要发送KVO通知。这次就不多说了,直接上代码。
//
// ConcurrentOperation.h
// IOSTest
//
// Created by 朱国清 on 17/1/11.
// Copyright © 2017年 朱国清. All rights reserved.
// #import <Foundation/Foundation.h> @interface ConcurrentOperation : NSOperation @end
//
// ConcurrentOperation.m
// IOSTest
//
// Created by 朱国清 on 17/1/11.
// Copyright © 2017年 朱国清. All rights reserved.
// #import "ConcurrentOperation.h" @interface ConcurrentOperation(){
BOOL executing;
BOOL finished;
}
- (void)completeOperation;
@end @implementation ConcurrentOperation
-(instancetype)init{
if (self=[super init]) {
executing = NO;
finished = NO;
}
return self;
}
-(BOOL)isExecuting{
return executing;
}
-(BOOL)isFinished{
return finished;
}
-(BOOL)isConcurrent{
return YES;
}
-(void)start{
if (self.cancelled) {
// 这样做才能让依赖它的operation知道已经完成了。
[self completeOperation];
return ;
}
[self willChangeValueForKey:@"isExecuting"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[self main];
});
executing = YES;
[self didChangeValueForKey:@"isExecuting"];
}
-(void)main{
NSLog(@"operation start.");
[NSThread sleepForTimeInterval:];
NSLog(@"operation end.");
[self completeOperation];
}
-(void)completeOperation{ [self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"]; executing = NO;
finished = YES; [self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"]; }
@end
四、通过Operation Queues运行Operation对象
一个operation对象,只有被添加到Operation Queue之后才能够有机会被执行的。这节我们就来学习Operation Queue对象。
Operation Queue对象主要功能是运行与管理Operaion对象的。当我们成功创建一个Operation Queue对象后,就可以给这个queue对象添加operation
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSBlockOperation * operationA = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation block A(1).");
}];
[queue addOperation:operationA];
queue对象也支持直接添加block。它的内部原理是用一个operation封装block,再把这个operation对象添加到queue。
[queue addOperationWithBlock:^{
NSLog(@"wrap block .");
}];
我们还可以用下面这个函数为queue添加一组block,这个函数的第二个参数如果为true的话,会阻塞当前线程直到第一个参数指定的operations都执行完成才返回
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait
当然也可以用下面这个函数阻塞当前线程直到queue的所以operation都执行完成。
- (void)waitUntilAllOperationsAreFinished
有时候我们不需要继续执行剩下的任务了,不如正在执行时用户退出了,可以下面这个方法取消所有的operation。
- (void)cancelAllOperations
五、手动运行Operation对象
大部分时间我们应该不会手动运行operation对象,不过了解多一点技术总是不会错的。我们通过看代码就给知道Operation queue是怎么运行我们的operation对象的。
- (BOOL)performOperation:(NSOperation*)anOp
{
BOOL ranIt = NO;
// 先判断是否准备好,是否被取消
if ([anOp isReady] && ![anOp isCancelled])
{
// 串行的话直接运行
if (![anOp isConcurrent]){
[anOp start];
}
// 并行的话开一个线程
else{
[NSThread detachNewThreadSelector:@selector(start) toTarget:anOp withObject:nil];
}
ranIt = YES;
}
else if ([anOp isCancelled])
{
// If it was canceled before it was started,
// move the operation to the finished state.
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
executing = NO;
finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"]; // Set ranIt to YES to prevent the operation from
// being passed to this method again in the future.
ranIt = YES;
}
return ranIt;
}
operation queue还有其他属性
maxConcurrentOperationCount - 最大并行的operation数量,如果设置为1时就是一个串行queue。
@property(readonly) NSUInteger operationCount - queue当前operation的数量
NSOperation 开发的更多相关文章
- iOS-三方框架AFNetworking基本使用
AFNetworking 是基于NSURLConnection, NSOperation开发的一款三方框架,主要用于处理一些关于网络请求上的业务,下文会简单介绍框架中经常使用的功能,如文件的上传,下载 ...
- iOS开发-NSOperation与GCD区别
Mac OS X 10.6及iOS4.0之后导入了可以使全体线程更高效运行,并且使并行处理应用更易开发的架构,GCD(Grand Central Dispatch),同时引入的还有Run Loop, ...
- iOS开发之多线程技术——NSOperation篇
本篇将从四个方面对iOS开发中使用到的NSOperation技术进行讲解: 一.什么是NSOperation 二.我们为什么使用NSOperation 三.在实际开发中如何使用NSOperation ...
- iOS开发多线程篇—NSOperation简单介绍
iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现 ...
- iOS开发多线程篇—NSOperation基本操作
iOS开发多线程篇—NSOperation基本操作 一.并发数 (1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3 (2)最大并发数:同一时间最多只能执行的任务的个数. ...
- iOS开发多线程篇—自定义NSOperation
iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UI ...
- iOS开发笔记5:多线程之NSThread、NSOperation及GCD
这篇主要总结下iOS开发中多线程的使用,多线程开发一般使用NSThread.NSOperation及GCD三种方式,常用GCD及NSOperation. 1.NSThread 创建线程主要有以下三种方 ...
- iOS开发——多线程OC篇&(十)多线程NSOperation基本使用
NSOperation基本操作 一.并发数 (1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3 (2)最大并发数:同一时间最多只能执行的任务的个数. (3)最⼤大并发数的 ...
- iOS多线程开发--NSThread NSOperation GCD
多线程 当用户播放音频.下载资源.进行图像处理时往往希望做这些事情的时候其他操作不会被中 断或者希望这些操作过程中更加顺畅.在单线程中一个线程只能做一件事情,一件事情处理不完另一件事就不能开始,这样势 ...
随机推荐
- C# break语句
一.C# break语句 break语句用于终止它后面的所有循环语句,使控制流程跳转到break语句所在层的外面,以便结束本层的所有循环.如果有多个循环语句进行嵌套,break语句则会跳到它所在层的外 ...
- 【Java】对象、类(抽象类与内部类)、接口
博文内容概况 对象和类 抽象类 接口 内部类 对象和类 对象是对客观事物的抽象,类是对对象的抽象.类是一种数据类型,其外观和行为由用户定义.类中可以设置两种类型的元素:字段(有时被称为数据成员)和方法 ...
- Lucene检索提高性能的几个方式
1.采用最新版本的Lucene 2.索引文件存储采用本地文件系统,如果需要挂载远程系统,请采用 readonly方式. 3.当然采用更好的硬件,更高I/O的磁盘 4.提高OS 缓存,调整参数 5.提高 ...
- 注释java中某个方法过时
添加一个注解即可 @Deprecated
- Nginx+php+mysql+wordpress搭建自己的博客站点
服务器环境要求Centos 6 或以上版本(由于我们的目标是半小时内搭建好,那就选简单yum安装)MySQL 5或更新版本Nginx 1或更新版本PHP 5 或更新版本 php-fpm 5或更新版本 ...
- python__系统 : 线程
线程之间,全局变量可以共享,但是局部变量依然是不共享的,线程的创建方式: threading.Thread(),还可以定义一个类继承Thread,重写他的run方法,具体和进程的写法一样. 那么,线程 ...
- php图片压缩-高清晰度
php高清晰度无损压缩 经常会用到把上传的大图片压缩,特别是体积,在微信等APP应用上,也默认都是有压缩的,那么,怎么样对图片大幅度压缩却仍能保持较高的清晰度呢? 压缩通常是有按比例缩放,和指定宽度压 ...
- Elasticsearch和Head插件安装
环境: CentOS7 Elasticsearch-6.3.2 JDK8 准备: JDK8 下载地址:http://www.oracle.com/technetwork/java/javase/do ...
- [Bzoj3991]寻宝游戏(dfs序+set)
Description 题目链接 Solution 用set按dfs序维护当前的宝物序列,那么答案为相邻2个点的距离加上头尾2个的距离 Code #include <cstdio> #in ...
- python-7面向对象高级编程
1-给类动态增加方法 class Student(object): pass def set_score(self, score): self.score = score Student.set_sc ...