当前位置: 首页 > news >正文

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:1]; 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-- > 0) { [NSThread sleepForTimeInterval:1]; 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, 0), ^{
        [self main];
    });
    executing = YES;
    [self didChangeValueForKey:@"isExecuting"];
}
-(void)main{
    NSLog(@"operation start.");
    [NSThread sleepForTimeInterval:2];
    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的数量

  

 

转载于:https://www.cnblogs.com/shuigu/p/6269366.html

相关文章:

  • 批量更新MongoDB的列。
  • SOA是什么
  • Apache HttpCore (理解IO基础)
  • 启动eclipse时出现“Failed to load the JNI shared library jvm.dll”错误及解决
  • 软件项目技术点(3)——多画布职责分离
  • 浅尝springboot中的Actuator包(一)
  • 【使用教程】论Windows下必备的抓包工具Fiddler2如何安装证书(查看Https)
  • RPC学习
  • js动画(三)
  • Django admin 自定制
  • B4X 大疆 dji开发
  • 微软Azure首席架构师John Gossman就微软加入Linux基金会一事答疑
  • mysql5.5以上my.ini中设置字符集
  • Codeforces 758A Holiday Of Equality
  • redhat配置caffe
  • [LeetCode] Wiggle Sort
  • Git同步原始仓库到Fork仓库中
  • Java深入 - 深入理解Java集合
  • JS题目及答案整理
  • Laravel Mix运行时关于es2015报错解决方案
  • PermissionScope Swift4 兼容问题
  • 从零搭建Koa2 Server
  • 关于for循环的简单归纳
  • 删除表内多余的重复数据
  • 使用权重正则化较少模型过拟合
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 终端用户监控:真实用户监控还是模拟监控?
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • 进程与线程(三)——进程/线程间通信
  • ​Linux·i2c驱动架构​
  • #include
  • #include到底该写在哪
  • (27)4.8 习题课
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (转)C语言家族扩展收藏 (转)C语言家族扩展
  • (转)Linux整合apache和tomcat构建Web服务器
  • (转)winform之ListView
  • * CIL library *(* CIL module *) : error LNK2005: _DllMain@12 already defined in mfcs120u.lib(dllmodu
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .NET Core WebAPI中封装Swagger配置
  • .Net MVC4 上传大文件,并保存表单
  • .Net 转战 Android 4.4 日常笔记(4)--按钮事件和国际化
  • .NET是什么
  • @EnableConfigurationProperties注解使用
  • @基于大模型的旅游路线推荐方案
  • @开发者,一文搞懂什么是 C# 计时器!
  • [ 隧道技术 ] 反弹shell的集中常见方式(二)bash反弹shell
  • [<MySQL优化总结>]
  • [C# 基础知识系列]专题十六:Linq介绍
  • [C#基础知识]专题十三:全面解析对象集合初始化器、匿名类型和隐式类型
  • [CSS] - 修正IE6不支持position:fixed的bug