MENU

NSOperation

April 23, 2016 • Read: 851 • Codes

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 的状态有五种:

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 模式下执行的。

Tags: iOS, iOS开发
Archives QR Code Tip
QR Code for this page
Tipping QR Code
Leave a Comment