摘要:本文将带你了解IOS开发入门iOS多线程之NSOperation概念及使用教程,希望本文对大家学IOS有所帮助。
本文将带你了解IOS开发入门iOS多线程之NSOperation概念及使用教程,希望本文对大家学IOS有所帮助。
iOS多线程之NSOperation概念及使用教程。
NSOperation&&NSOperationQueue的使用姿势全解
经过前面的学习,讲解了最基础的NSThread使用方法,封装更完善的GCD,GCD提供了极其便捷的方法来编写多线程程序,可以自动实现多核的真正并行计算,自动管理线程的生命周期,好处不言而喻,但可定制性就有点不足了,Foundation框架提供了NSOperation和NSOperationQueue这一面向对象的多线程类,这两个类与GCD提供的功能类似,NSOperation提供任务的封装,NSOperationQueue顾名思义,提供执行队列,可以自动实现多核并行计算,自动管理线程的生命周期,如果是并发的情况,其底层也使用线程池模型来管理,基本上可以说这两个类提供的功能覆盖了GCD,并且提供了更多可定制的开发方式,开发者可以按需选择。
使用NSOperation和NSOperationQueue来编写多线程程序非常简单,只需要创建一个任务对象,创建一个执行队列或者和获取主线程一样获取一个主任务队列,然后将任务提交给队列即可实现并发,如过你想要串行只需要将队列的并发数设置为一即可。接下来将分别介绍两个类的使用。
NSOperation “任务的封装”
和GCD类似,GCD向队列提交任务,NSOperation就是对任务进行的封装,封装好的任务交给不同的NSOperationQueue即可进行串行队列的执行或并发队列的执行。这里的任务就是NSOperation类的一个方法,main方法或start方法(两个方法有区别,后文会讲),但NSOperation类的这两个方法是空方法,没有干任何事情,因此,我们需要自定义继承NSOperation类并重写相关方法,OC也提供了两个子类供我们使用NSBlockOperation和NSInvocationOperation。
接下来看一下NSOperation类中比较重要的属性和方法:
/*
对于并发Operation需要重写该方法
也可以不把operation加入到队列中,手动触发执行,与调用普通方法一样
*/
- (void)start;
/*
非并发Operation需要重写该方法
*/
- (void)main;
//只读属性任务是否取消,如果自定义子类,需要重写该属性
@property (readonly, getter=isCancelled) BOOL cancelled;
/*
设置cancelled属性为YES
仅仅标记cancelled属性,不退出任务,和NSThread的cancel一个机制
自定义子类时需要使用该属性判断是否在外部触发了取消任务的操作,手动退出任务
*/
- (void)cancel;
//只读属性,任务是否正在执行,如果自定义子类,需要重写该属性
@property (readonly, getter=isExecuting) BOOL executing;
/*
只读属性,任务是否结束,如果自定义子类,需要重写该方法
对于加入到队列的任务来说,当finished设置为YES后,队列会将任务移除出队列
*/
@property (readonly, getter=isFinished) BOOL finished;
//是否为并发任务,该属性已经被标识即将弃用,应该使用下面的asynchronous属性
@property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below
/*
只读属性,判断任务是否为并发任务,默认返回NO
如果需要自定义并发任务子类,需要重写getter方法返回YES
*/
@property (readonly, getter=isAsynchronous) BOOL asynchronous;
/*
只读属性,任务是否准备就绪
对于加入队列的任务,当ready为YES,标识该任务即将开始执行
如果任务有依赖的任务没有执行完成ready为NO
*/
@property (readonly, getter=isReady) BOOL ready;
/*
添加一个NSOperation为当前任务的依赖
如果一个任务有依赖,需要等待依赖的任务执行完成才能开始执行
*/
- (void)addDependency:(NSOperation *)op;
//删除一个依赖
- (void)removeDependency:(NSOperation *)op;
//任务在队列里的优先级
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
//任务在队列里的优先级
@property NSOperationQueuePriority queuePriority;
/*
任务完成后的回调方法
当finished属性设置为YES时才会执行该回调
*/
@property (nullable, copy) void (^completionBlock)(void);
上述内容中有一些属性和方法是在自定义NSOperation子类中必须要重写的,自定义子类能够提供更高的可定制性,因此,编写自定义子类更复杂,自定义子类在后面会讲,如果我们只需要实现GCD那样的功能,提交一个并发的任务,OC为我们提供了两个子类NSBlockOperation和NSInvocationOperation,这两个子类已经帮我们完成了各种属性的设置操作,我们只需要编写一个任务的block或者一个方法即可像使用GCD一样方便的编写多线程程序。
接下来举两个创建任务的栗子:
//创建一个NSBlockOperation对象,传入一个block
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 100; i++)
{
NSLog(@"Task1 %@ %d", [NSThread currentThread], i);
}
}];
/*
创建一个NSInvocationOperation对象,指定执行的对象和方法
该方法可以接收一个参数即object
*/
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task:) object:@"Hello, World!"];
可以发现,创建任务真的很简单,就像GCD中创建任务一样简洁,任务创建完成就可以创建队列了。
NSOperationQueue
NSOperationQueue就是任务的执行队列,看一下该类中有哪些比较重要的属性和方法:
//向队列中添加一个任务
- (void)addOperation:(NSOperation *)op;
/*
向队列中添加一组任务
是否等待任务完成,如果YES,则阻塞当前线程直到所有任务完成
如果为False,不阻塞当前线程
*/
- (void)addOperations:(NSArray<nsoperation> *)ops waitUntilFinished:(BOOL)wait;
//向队列中添加一个任务,任务以block的形式传入,使用更方便
- (void)addOperationWithBlock:(void (^)(void))block;
//获取队列中的所有任务
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
//获取队列中的任务数量
@property (readonly) NSUInteger operationCount;
/*
队列支持的最大任务并发数
如果为1,则只支持同时处理一个任务,即串行队列,主队列就是串行队列使用主线程执行任务
如果为大于1的数,则支持同时处理多个任务,即并发队列,底层使用线程池管理多个线程来执行任务
*/
@property NSInteger maxConcurrentOperationCount;
//队列是否挂起
@property (getter=isSuspended) BOOL suspended;
//队列的名称
@property (nullable, copy) NSString *name;
/*
取消队列中的所有任务
即所有任务都执行cancel方法,所有任务的cancelled属性都置为YES
*/
- (void)cancelAllOperations;
//阻塞当前线程直到所有任务完成
- (void)waitUntilAllOperationsAreFinished;
//类属性,获取当前队列
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue;
//类属性,获取主队列,任务添加到主队列就会使用主线程执行,主队列的任务并发数为1,即串行队列
@property (class, readonly, strong) NSOperationQueue *mainQueue;</nsoperation>
上述属性中比较重要的就是maxConcurrentOperationCount,该属性直接决定了队列是串行的还是并发的,接下来看一个栗子:
- (void)task:(id)obj
{
for (int i = 0; i < 100; i++)
{
NSLog(@"Task2 %@ %d %@", [NSThread currentThread], i, obj);
}
}
- (void)viewWillAppear:(BOOL)animated
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:2];
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 100; i++)
{
NSLog(@"Task1 %@ %d", [NSThread currentThread], i);
}
}];
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task:) object:@"Hello, World!"];
[queue addOperation:operation];
[queue addOperation:invocationOperation];
}
上面这个栗子就很简单了,首先创建了一个队列,最大任务并发数设置为2,接下来创建了两个任务并添加进了队列,摘取几个输出如下:
Task2 <nsthread: 0x1c427e440="">{number = 3, name = (null)} 0 Hello, World!
Task1 <nsthread: 0x1c0462fc0="">{number = 4, name = (null)} 0</nsthread:></nsthread:>
从输出中可以发现,两个任务使用了两个不同的线程来执行,如果将最大任务并发数量设置为1这里就会使用同一个线程串行执行,任务2必须得等任务1执行完成才能开始执行,就不再做实验了。使用Foundation提供的NSBlockOperation和NSInvocationOperation很方便,这两个子类已经帮我们完成了各个重要属性的设置操作,当block或传入的方法任务在执行时会设置executing属性值为YES,执行完成后将executing设置为NO并将finished设置为YES,但是,如果在block中使用另一个线程或是GCD异步执行任务,block或方法会立即返回,此时就会将finished设置为YES,但是其实任务并没有完成,所以这种情况下不能使用该属性,当需要更高定制性时需要使用自定义NSOperation子类。
这个栗子很简单,效果就和我们使用GCD编写的多线程程序一样,接下来再举个添加依赖的栗子:
<div class="line number16 index15 a
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号