聊一聊NSOperation的那些事
在iOS开发中,多线程技术用到最多的就是GCD和NSOperation,上一篇文章已经对GCD有了全面的了解,这篇文章简单的聊一聊NSOperation。
一、简介
- 除了,NSThread和GCD实现多线程,配合使用NSOperation和NSOperationQueue也能实现多线程编程
NSOperation和NSOperationQueue
实现多线程的具体步骤
- 1、先将需要执行的操作封装到一个NSOperation的子类对象中
- 实际上,NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
- 2、然后将NSOperation对象添加到NSOperationQueue中
- 3、系统会自动将NSOperationQueue中的NSOperation取出来
- 4、将取出的NSOperation封装的操作放到一条新线程中执行
二、NSOperation
- 如上所示:要实现多线程,必须要将执行的操作封装到NSOperation的子类对象中,那么NSOperation的子类有哪些?
1、使用NSOperation子类的方式有3种
- NSInvocationOperation
- NSBlockOperation
- 自定义子类继承NSOperation,实现NSOperation的某些方法
NSInvocationOperation
- 创建NSInvocationOperation对象
|
NSInvocationOperation
将操作封装成SEL
,只有将NSInvocationOperation
添加到NSOperationQueue
中,才会异步执行操作。
|
NSBlockOperation
- 创建NSBlockOperation 对象
|
NSBlockOperation
将任务封装成一个block,这种方式看起来更方便。当然,你还可以调用-addExecutionBlock:
方法,添加另一个任务。
注意点:只要NSBlockOperation
封装的操作数 >1,直接调用start
方法,就会异步执行操作,但是默认的初始化的block
任务是在主线程中执行。
|
自定义 NSOperation
首先需要继承NSOperation
,然后实现某些方法。
我们有两种方法来实现自定义operation
,一种是通过重写main
方法,一种是通过重写start
方法,前者实现起来更简单,我们不需要管理一些属性状态,例如isExecuting
或者isFinished
等。当main
方法执行结束的时候,这个operation
就结束了。另外一种是重写start
方法,但是这种方法需要你管理各种属性的状态,虽然麻烦,但是相对灵活。如果你同时实现start
和main
方法,则优先使用start
方法。因为第一种方法比较简单,我们这里以第二种方法来自定义operation
。
首先,父类提供了一系列的getter
方法,用以获知任务的状态。
@property (readonly, getter=isReady) BOOL ready
此属性表明NSOperation
是否已经准备好开始执行任务了,如果返回false
,NSOperation
的之后的方法都不会执行。通常情况下,自定义operation,我们可以利用它来让调用者必须满足一定的条件才能执行任务。@property (readonly, getter=isExecuting) BOOL executing
是否正在执行。@property (readonly, getter=isFinished) BOOL finished
任务是否完成@property (readonly, getter=isCancelled) BOOL cancelled
任务是否取消@property (readonly, getter=isConcurrent) BOOL concurrent
表示是否异步执行任务
自定义operation
如下
MyOperation.h
|
MyOperation.m
|
这仅仅是一个简单的实例,真正的项目中还需要打磨,如果没有特殊的要求,还是使用系统提供的比较好。
添加依赖
目的 -> NSOperation之间可以设置依赖来保证执行顺序
例如:一定要让操作A执行完后,才能执行操作B,可以这么写
|
例如我们通过不同的线程下载图片 当都下载完成以后在合成图片
|
三、NSOperationQueue
NSOperation
可以调用start
方法来执行操作,但是默认是同步执行的。将NSOperation
添加到NSOperationQueue
中,系统会自动异步调用NSOperation
的start
方法或者main
方法来执行任务。
NSOperationQueue
提供了三种添加任务的方法:
- (void)addOperation:(NSOperation *)op
将NSOperation对象添加到队列中- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait
将NSOperation对象数组添加到队列中,wait表示是否阻塞当前队列,YES表明以后添加的任务只能在ops中的所有任务执行完成以后才会执行,NO表示不阻塞当前队列- (void)addOperationWithBlock:(void (^)(void))block
将一个block添加到任务中。
这里有几点需要注意的,添加的任务一定不能是正在执行的或者已经完成的,可通过executing
和finished
来判断。不能重复添加一个operation
到同一个队列中,否则会报NSInvalidArgumentException异常。
再来看看NSOperationQueue
的属性:
operations
当前正在执行或者等待执行的任务的集合,如果一个任务执行完毕,会从中删除。operationCount
当前队列中的任务数(正在执行或者等待执行)maxConcurrentOperationCount
最大并发数,默认是-1,即无限大,如果设置为1,那么当前队列即为串行队列,但不能设置成0,如果设置成0,那么当前的所有任务都不能执行。name
队列的名字
当然,NSOperationQueue
也提供了取消操作,- (void)cancelAllOperations
,此方法会取消所有未执行的操作。内部实现是分别调用任务的cancel
方法。但是已经执行的操作将会继续执行。另外此方法并不会将取消的操作从队列中移除。但是实际却移除了,这是因为NSInvocationOperation
和NSBlockOperation
内部自己管理了finish
状态,在cancel
方法中会将当前任务的finish
设置为YES
。
- (void)waitUntilAllOperationsAreFinished
阻塞当前任务队列。