NSOperation

NSOperation 是Objective-C 提供的一种面向对象的多线程编程方式。在项目开发中,多线程通常使用两种方式,GCD 和 NSOperation。GCD的使用相对来说更多一些,因此本文介绍下NSOperation,以及GCD和NSOperation的使用场景区别。

NSOperation的使用

先看下官方文档对NSOperation的介绍:

An abstract class that represents the code and data associated with a single task.

NSOperation本身是一个抽象类,是不能直接使用的,需要子类化之后才能够使用。系统提供的有两个子类:NSBlockOperation 和 NSInvocationOperation。不过,通常在使用NSOperation时,都是自定义子类,而不是直接使用系统提供的两个子类。

NSOperation和NSOperationQueue结合使用,能够实现多线程编程。关于NSOperationQueue,下面会介绍。

NSOperation 中的方法

既然自定义类继承于 NSOperation,那么子类需要重写父类的哪些方法呢?在回到这个问题之前,先看一下NSOperation 提供的方法。

- (void)start;
- (void)main;
- (void)cancel;

NSOperation.h中主要是这三个方法。由于这三个方法没有注释,因此还是看一下这三个方法的官方文档介绍,了解一下这三个方法的作用。

start方法

Begins the execution of the operation.
The default implementation of this method updates the execution state of the operation and calls the receiver’s main method. This method also performs several checks to ensure that the operation can actually run.

根据文档介绍可以了解到start方法的作用以及内部的默认实现。start方法的作用是开始执行这个operation。

start方法的默认实现中,在operation执行的过程中,会改变operation的状态,而且,start方法会调用 main方法。start方法同时也会做一些检测,以确保该operation真的可以执行。

上面提到start方法中会调用main方法,那么main方法中又做了什么操作?

main方法

Performs the receiver’s non-concurrent task.
The default implementation of this method does nothing. You should override this method to perform the desired task. In your implementation, do not invoke super.

If you are implementing a concurrent operation, you are not required to override this method but may do so if you plan to call it from your custom start method.

main方法的作用:执行非并行(串行)的任务。

main方法的默认实现是空的。在子类中应该重写main方法,在main方法中做该operation 期望的操作。而且在main方法中不要调用super 的main方法。NSOperation如果是一个串行的,那么需要重写main方法,如果实现的是一个并行的任务,则不需要重写main方法。

PS:文档真的非常有用,不仅仅会介绍方法的作用,而且会告诉我们方法的使用场景。

cancel方法

Advises the operation object that it should stop executing its task.
This method does not force your operation code to stop. Instead, it updates the object’s internal flags to reflect the change in state.

cancel方法的作用是修改operation的状态。

NSOperation 的几种状态

上面已经多次提到了NSOperation的状态,那么NSOperation都有哪些状态呢?

看一下NSOperation.h中的定义:

@property (readonly, getter=isCancelled) BOOL cancelled;

@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isReady) BOOL ready;

isCancelled、isExecuting、isFinished、isReady,NSOperation的四个状态:是否被取消、是否正在执行、是否执行完毕、是否准备好执行。看一下NSOperation的四个状态。

isCancelled

A Boolean value indicating whether the operation has been cancelled
The default value of this property is NO. Calling the cancel method of this object sets the value of this property to YES. Once canceled, an operation must move to the finished state.

You should always check the value of this property before doing any work towards accomplishing the operation’s task, which typically means checking it at the beginning of your custom main method.

作用:标记operation是否被取消了。默认值是NO。调用 cancel 方法后,该值会变为YES。一旦一个operation被取消了,他的finish状态必须变成YES。

在开始执行一个operation之前,应该检查一下isCancelled是否为YES,也就是说,在自定义NSOperation的子类的main方法中,应该首先检查isCancelled,如果operation被取消了,那么就没有执行的必要了。

isReady

A Boolean value indicating whether the operation can be performed now.
The readiness of operations is determined by their dependencies on other operations and potentially by custom conditions that you define.

标记operation是否准备好执行的bool值。值取决于该operation的依赖关系,以及开发者自定义的条件。

If you want to use custom conditions to define the readiness of your operation object, reimplement this property and return a value that accurately reflects the readiness of the receiver. If you do so, your custom implementation must get the default property value from super and incorporate that readiness value into the new value of the property. In your custom implementation, you must generate KVO notifications for the isReady key path whenever the ready state of your operation object changes.

如果开发者想要使用自定义的条件来决定operation isReady的值,那么需要重新实现该属性,如果这样做了,自定义的实现中,必须将[super isReady]和自定义的条件组合起来,两者都为YES,才可以返回YES。另外需要注意的是,如果自定义实现了该方法,当ready值改变时,需要手动触发KVO通知

isExecuting

A Boolean value indicating whether the operation is currently executing.

标记operation是否正在执行的bool值。

When implementing a concurrent operation object, you must override the implementation of this property so that you can return the execution state of your operation. In your custom implementation, you must generate KVO notifications for the isExecuting key path whenever the execution state of your operation object changes.

You do not need to reimplement this property for nonconcurrent operations.

当子类是一个并发operation时,必须重新实现该方法。在自定义实现中,当isExecuting的值改变时,需要手动触发KVO通知。

如果operation是串行的,则没有必要重写isExecuting方法。

isFinished

A Boolean value indicating whether the operation has finished executing its task.

标记operation是否执行完毕的bool值。

When implementing a concurrent operation object, you must override the implementation of this property so that you can return the finished state of your operation. In your custom implementation, you must generate KVO notifications for the isFinished key path whenever the finished state of your operation object changes.

You do not need to reimplement this property for nonconcurrent operations.

当子类operation是并行时,必须重新实现isFinished方法。在自定义实现中,如果isFinished的值改变了,必须触发KVO通知。

如果NSOperation是串行的,则没必要重写isFinished方法。

同步NSOperation的实现

其实呢,根据上面的介绍,已经可以看出同步NSOperation和异步NSOperation的实现是有较大的差别的,而且同步NSOperation的实现要简单一些。

既然是同步的,start方法结束时,operation也就执行完毕了,而start方法在执行过程中会调用main方法,因此通常情况下,同步NSOperation只需要重写main方法就可以。main方法中实现operation真正要执行的操作。

举个例子:

// 1.main方法中不需要调用 [super main]
// 2.在main开始之前,先检查operation是否被取消了
- (void)main {
    if ([self isCancelled]) return;
    // do somthing
}
异步NSOperation的实现

对于异步NSOperation来说,start方法执行完毕后,operation可能还没有执行完毕。因此,默认的start方法是不能满足异步NSOperation的需求的。

对于一个异步NSOperation来说,通常需要重写的方法有:

start方法:需要在start方法中将operation放在其他线程执行,且需要改变operation的状态。

isExecuting 和 isFinished。这两个方法上面已经介绍过了,而且官方文档中明确说明了什么时候需要重写,什么时候不需要重写。

举个例子:

- (void)start {
    @autoreleasepool {
        if ([self isCancelled]) {
            // 先判断是否任务已经被取消了,如果已经被取消,则调用 _cancelOperation,且置 finished为YES
            [self performSelector:@selector(_cancelOperation) onThread:[[self class] _networkThread] withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
            self.finished = YES;
        } else if ([self isReady] && ![self isFinished] && ![self isExecuting]) {
            // 判断条件  isReady 且  还未结束  且 没有正在执行
            // 改变executing的状态
            self.executing = YES;
            // 在其他线程上执行任务
            [self performSelector:@selector(_startOperation) onThread:[[self class] _networkThread] withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];
        }
    }
}
- (void)setFinished:(BOOL)finished {
    if (_finished != finished) {
        // 触发kvo
        [self willChangeValueForKey:@"isFinished"];
        _finished = finished;
        [self didChangeValueForKey:@"isFinished"];
    }
}

- (BOOL)isFinished {
    BOOL finished = _finished;
    return finished;
}

isExecuting的代码和isFinished的代码类似。

NSOperation的依赖关系

上面在介绍start方法时提到了NSOperation的依赖。可以在不同的operation之间添加依赖关系。假设operationA依赖 operationB,那么只有operationB执行完毕后,operationA的isReady 才为YES,也就是说,依赖关系能够决定operation的执行顺序。

需要注意的是,依赖是operation与operation之间的关系,即使operation处在不同的NSOperationQueue中,依赖关系也是生效的。

NSOperation的优先级

NSOperation的定义中有一个属性是:queuePriority。queuePriority是一个枚举类型,其可取的值有:

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

该值表示了一个operation的优先级。在一个队列中,优先级高的operation要先于优先级低的operation执行。

NSOperationQueue

NSOperationQueue 用于管理operation。一个NSOperation开始执行有两种方式:一种是直接调用start方法,另外一种就是添加到NSOperationQueue中。

NSOperationQueue中的一些方法和属性:

@property NSInteger maxConcurrentOperationCount;
- (void)cancelAllOperations;
@property (getter=isSuspended) BOOL suspended;
maxConcurrentOperationCount

NSOperationQueue中最大的并发量。当maxConcurrentOperationCount的值为1时,operation是一个一个执行,实际上就是一个串行的队列。

cancelAllOperations

取消队列中所有的operation。根据上面的介绍,实际上是队列中所有的operation执行cancel方法,也就是将isCancelled置为YES。

suspended

如果suspended为YES,那么NSOperationQueue不会执行新的operation。

NSOperation vs GCD

在实际的项目开发中,使用GCD的情况可能会多一些,但是在一些特殊的场景中,使用NSOperation可能会更方便,比如说网络请求。那么NSOperation相对于GCD来说,有什么优势呢?

  1. NSOperation和NSOperationQueue结合使用能够设置最大的并发量,GCD是不可以的。
  2. NSOperation支持取消操作,而GCD不支持。
  3. 同一个NSOperationQueue中的NSOperation可以设置不同的优先级,而GCD只能设置队列的优先级。
  4. 使用NSOperation可以得到该operation的状态,且状态改变时会触发KVO通知,这一点GCD是不具备的。
  5. operation与operation之间支持设置依赖关系,以保证operation执行的先后顺序。GCD不支持依赖关系。

完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值