NSOperation 是一个抽象基类,其主要是为了提供接口,以及封装了一些内部实现。通常情况下我们会使用其子类来完成我们想要的操作,而不是直接创建一个 NSOperation 的实例,毕竟它并没有提供可以执行我们代码的接口,所以说它「几乎什么都做不了」。
iOS 中提供了一些 NSOperation 的子类,如 NSInvocationOperation、NSBlockOperation 等等,必要的话,我们也可以自己写一个 NSOperation 的子类,来完成自定义操作。
如果(我们)没有做一些特殊的修改的话,这些子类的使用方法应该是一样的,都是使用 NSOperation 默认的方法(接口)和执行流程。
NSOperation 的执行过程
我们先看一下 NSOperation 都有哪些方法吧。
首先看一下 NSOperation 的控制方法:
// 启动NSOperation
- (void)start;
// 取消NSOperation的执行
- (void)cancel;
// 阻塞当前线程,直到该NSOperation结束
- (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0);当一个 NSOperation(或其子类)的实例创建完成后,可以调用start方法启动该 NSOperation。在 NSOperation 启动后,其首先会设置 NSOperation 的状态,然后便会调用main方法:
- (void)main;main方法中存放了 NSOperation 的任务代码,默认情况下main方法什么也不做,所以我们在创建 NSOperation 子类的时候需要重写此方法。main方法会在 NSOperation 提供的 AutoreleasePool 中执行,所以通常情况下,我们不需要担心内存管理的问题。
需要注意的是,默认情况下,NSOperation 会在调用start方法的线程中执行,也就是说,如果我们在主线程中调用了start方法,那么这个 NSOperation 及其其他的方法包括main方法等便都在主线程中执行了。所以通常情况下我们不会直接调用 NSOperation 的start方法,而是将其放进 NSOperationQueue 中由 NSOperationQueue 控制 NSOperation 的执行。
NSOperation 的依赖关系
// 添加依赖
- (void)addDependency:(NSOperation *)op;
// 移除依赖
- (void)removeDependency:(NSOperation *)op;
// 所有的依赖
@property (readonly, copy) NSArray<NSOperation *> *dependencies;一个 NSOperation 实例可以依赖另一个 NSOperation 实例,只有当依赖的实例完成后,这个 NSOperation 才可以开始执行(状态才可以成为 Ready)。
我们可以使用- addDependency:方法为一个 NSOperation 实例添加依赖,但注意不要写出循环依赖,否则两个 NSOperation 就都没有开始执行的机会了。另外,即使依赖链中的 NSOperation 实例已经完成,也不会从依赖链中移除。要移除一个依赖,我们可以使用- removeDependency:方法。
NSOperation 的状态
NSOperation 的状态有五种:
状态的查询可以通过以下属性进行:
@property (readonly, getter=isCancelled) BOOL cancelled;
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isReady) BOOL ready;在 NSOperation 实例创建完成后,其状态即为 Pending 状态,在其所有依赖均完成后便会进入 Ready 状态,在此时,NSOperation 实例才可以开始执行。开始执行后,状态会变成 Executing,执行完毕后,状态为 Finished。当调用cancel方法后其状态为 Cancelled。
当 NSOperation 实例执行完毕后,便会调用其completionBlock属性:
@property (nullable, copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);在 iOS8 及以上版本,completionBlock开始执行后,其会被置nil。
NSOperation 的优先级
NSOperation 的优先级有:
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};可以通过设置queuePriority属性来进行修改。优先级高的实例会被优先执行。
NSInvocationOperation 的创建
NSInvocationOperation 的初始化方法有两个:
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
- (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;示例:
NSInvocationOperation* op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thingsTodo:) object:@"Hello"];target 和 selector 就不说了,object 为 selector 方法传入的属性,可以为 nil。所以,selector 指向的方法最多只能有一个参数,否则会在执行是抛出method 'xxx' requires more than 1 argument的警告。
另外,NSInvocationOperation 及 NSInvocation 在 Swift 中已不可用。
NSBlockOperation 的创建
创建方法:
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;示例:
NSBlockOperation* block = [NSBlockOperation blockOperationWithBlock:^{
//Things to do...
NSLog(@"Hello");
}];另外,NSBlockOperation 还提供了以下方法和属性:
// 追加executionBlock
- (void)addExecutionBlock:(void (^)(void))block;
// 查看所有executionBlocks,注意这里属性为copy
@property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;NSOperationQueue
创建
NSOperationQueue 的创建不需要任何参数:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];另外,NSOperationQueue 还提供了一些类方法以获取已经存在的一些 Queue:
// 当前Queue
+ (nullable NSOperationQueue *)currentQueue NS_AVAILABLE(10_6, 4_0);
// Main Queue
+ (NSOperationQueue *)mainQueue NS_AVAILABLE(10_6, 4_0);添加 Operations
- (void)addOperation:(NSOperation *)op;
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);Operation 的一些属性
// 当前Queue中(未执行完成)的operations,当一个Operation执行完毕后会从其中移除。
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
// 当前Queue中(未执行完成)的operations的数量
@property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);
// 最大并发执行的数量
@property NSInteger maxConcurrentOperationCount;
// 队列名称,方便调试等。
@property (nullable, copy) NSString *name NS_AVAILABLE(10_6, 4_0);
// 是否暂停将要执行的operation,但不会暂停已开始的operation
// 或返回是否已暂停
@property (getter=isSuspended) BOOL suspended;
//取消所有Operation的执行(向每一个Operation调用cancel 方法)
- (void)cancelAllOperations;
// 等待所有Operations执行完成
- (void)waitUntilAllOperationsAreFinished;Main Queue
Main Queue 中的所有 Operation 全都会在主线程上执行,其仅能顺序执行,不可以并发执行。主线程是在 Common Runloop Mode 模式下执行的。