NSOperation-简介
常用子类:
NSOperation是一个基类,不应该直接生成NSOperation对象,而是应该用它的子类。
- NSInvocationOperation
- 将特定对象的特定方法封装成NSOperation
- NSBlockOperation
- 将代码块封装成NSOpreation
示例:
创建NSInvocationOperation
1 |
|
创建NSBlockOperation
1 |
|
常用状态:
可以通过KVO监听NSOperation的状态
- isCancelled
- isAsynchronous
- isExecuting
- isFinished
- isReady
执行任务
创建了一个NSBlockOperation,并且设置好它的block,也就是将要执行的任务。这个任务会在主线程中执行。
调用start方法让NSOperation方法运行起来。start是一个同步方法。
在当前任务状态和依赖关系合适的情况下,启动NSOperation的main方法任务,需要注意缺省实现只是在当前线程运行。如果需要并发执行,子类必须重写这个方法,并且使 - (BOOL)isConcurrent 方法返回YES
1
2
3
4
5
6
7
8
let operation = NSBlockOperation { () -> Void in
print(NSThread.currentThread())
}
operation.addExecutionBlock { () -> Void in
print("execution block1 --(NSThread.currentThread())")
}
operation.start()
默认的NSOperation是同步执行的。简单的看一下NSOperation类的定义会发现它有一个只读属性asynchronous
这意味着如果想要异步执行,就需要自定义NSOperation的子类。或者使用NSOperationQueue
取消任务
如果我们有两次网络请求,第二次请求会用到第一次的数据。如果此时网络情况不好,第一次请求超时了,那么第二次请求也没有必要发送了。当然,用户也有可能人为地取消某个NSOperation。
当某个NSOperation被取消时,我们应该尽可能的清除NSOperation内部的数据并且把cancelled和finished设为true,把executing设为false。
1 |
|
获取状态
- @property(readonly, getter=isCancelled) BOOL cancelled
- 当前任务状态是否已标记为取消
- @property(readonly, getter=isExecuting) BOOL executing
- 当前任务状态是否已标记为取消
- @property(readonly, getter=isFinished) BOOL finished
- NSOperation任务是否已结束
- @property(readonly, getter=isConcurrent) BOOL concurrent
- @property(readonly, getter=isAsynchronous) BOOL asynchronous
- @property(readonly, getter=isReady) BOOL ready
- NSOperation任务是否已结束
- @property(copy) NSString *name
等待
-(void)waitUntilFinished
1
2
3
4
5
NSBlockOperation *opB = [NSBlockOperation blockOperationWithBlock:^{
[opA waitUntilFinished]; //opB线程等待直到opA执行结束(正常结束或被取消)
[self operate];
}];
设置依赖
依然考虑刚刚所说的两次网络请求的例子。因为第二次请求会用到第一次的数据,所以我们要保证发出第二次请求的时候第一个请求已经执行完。但是我们同时还希望利用到NSOperationQueue的并发特性(因为可能不止这两个任务)。
这时候我们可以设置NSOperation之间的依赖关系。语法非常简洁:
1 | operation2.addDependency(operation1) |
需要注意的是NSOperation之间的相互依赖会导致死锁
移除依赖:
1 | - (void)removeDependency:(NSOperation *)operation |
优先级
GCD中,任务(block)是没有优先级的,而队列具有优先级。和GCD相反,我们一般考虑NSOperation的优先级
NSOperation有一个NSOperationQueuePriority枚举类型的属性queuePriority
1 | typedef enum : NSInteger { |
如果NSInvocationOperation和NSBlockOperation对象不能满足需求, 我们可以直接继承NSOperation, 并添加额外的功能。
继承所需的工作量主要取决于你要实现非并发还是并发的NSOperation。定义同步的NSOperation要简单许多,只需要重载-main这个方法,在这个方法里面执行主任务,并正确地响应取消事件; 对于异步NSOperation, 必须重写NSOperation的多个基本方法进行实现(main、start)。
自定义非并发NSOperation
- 重写NSOperation的
main
方法,该方法会作为NSOperation所启动线程的执行体- 在 operation 的 main 方法里面,必须提供 autorelease pool,因为你的 operation 完成后需要销毁。
- 实现KVO机制,一旦你的 operation 开始了,必须通过 KVO,告诉所有的监听者,现在该operation的执行状态。(即修改isFinished,isExecuting的值)
- 重载 isExecuting方法和isFinished方法。在这两个方法中,必须返回一个线程安全值(通常是个BOOL值),这个值可以在 operation 中进行操作。
示例代码:
.h文件:
1 |
|
.m文件:
1 |
|
调用
queue方式:
1 |
|
手动方式:
DownloadOperation *operation = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
// self.imageView.image = image;
}];
[operation start];
自定义并发NSOperation
- 和同步一样需要 重写mian start
- 和同步一样需要 使用KVO通知状态改变
- 和同步一样需要 重写isExecuting isFinishing
- 比同步多的是 重写isAsynchronous(或者isConcurrent)
示例代码:
1 |
|
对于非主队列来说,一旦一个NSOperation被放入其中,那这个NSOperation一定是并发执行的。 NSOperationQueue会为每一个NSOperation创建线程并调用它的start方法。
KVO键值观察
- operations - read-only
- operationCount - read-only
- maxConcurrentOperationCount - readable and writable
- suspended - readable and writable
- name - readable and writable
获取特殊NSOperationQueue
- +(NSOperationQueue *)currentQueue
- 返回当前NSOperationQueue,如果当前线程不是在NSOperationQueue上运行则返回nil
- +(NSOperationQueue *)mainQueue
- 返回主线程的NSOperationQueue
管理队列中的Operation
- -(void)addOperation:(NSOperation *)operation
- 加入到执行队列中,如果isReady则开始执行
- -(void)addOperations:(NSArray
*)ops waitUntilFinished:(BOOL)wait - 批量加入执行operation,wait标志是否当前线程等到所有operation结束
- -(void)addOperationWithBlock:(void (^)(void))block
- 相当于加入一个NSBlockOperation执行任务
- -(void)cancelAllOperations
- 相当于加入一个NSBlockOperation执行任务
- -(void)waitUntilAllOperationsAreFinished
- 当前线程等待,直到所有operation都执行结束
- @property(readonly, copy) NSArray <__kindof NSOperation > operations
- 返回已加入执行operation的数组,当某个operation结束后会自动从这个数组清除
- @property(readonly) NSUInteger operationCount
- 返回已加入执行operation的数目
- @property NSInteger maxConcurrentOperationCount
- 设置最大并发执行数,如果为1则同时只有一个并发任务在运行,可控制顺序执行关系
@property(getter=isSuspended) BOOL suspended
1
2queue.suspended = true //暂停queue中所有operation
queue.suspended = false //恢复queue中所有operation
简单使用:
1 |
|