NSOperationQueue 是调节操作执行的队列。
操作队列根据NSOperation
对象的优先级和准备情况执行排队的NSOperation
对象。operation添加到一个队列中之后,operation将保持在队列中,直到它完成任务。添加后,不能直接从队列中删除operation。
注意:
操作队列会retain 操作,直到它们完成。并且操作队列自身也会被retain,直到所有的操作完成。使用未完成的operation挂起 操作队列会导致内存泄漏
操作队列的更多信息可以查看官方文档
确定执行顺序
队列中的操作根据其准备情况,优先级和操作依赖性进行执行。 如果所有排队的操作都具有相同的 queuePriority, 并且 ready 属性返回 YES ,则会按照添加到队列的顺序执行。 否则,操作队列总是执行相对于其他操作具有最高优先级的操作。
我们不应该依赖队列来确保操作的特定执行顺序,因为操作准备情况的更改可能会改变执行顺序。 操作依赖为操作提供了一个绝对的执行顺序,即使这些操作位于不同的操作队列。 操作对象直到它依赖的所有的操作完成执行之前都不会被视为可以执行。
取消操作
结束任务并不一定意味着operation完成该任务,操作也可以被取消。取消一个operation对象会将其留在队列中,但是会通知对象尽可能快的 stop 其任务。对于正在执行的操作,这意味着operation对象的工作代码必须检查取消状态、停止正在做的事情,并标记自身为finished。 对于正在排队但尚未执行的操作,队列必须调用operation对象的start
方法,以便于他可以处理取消事件并将自身标记为finished.
注意:
取消一个操作会导致操作忽略它可能具有的依赖关系。可能会使队列尽可能快地执行operation的start方法。 start 方法反过来将操作转至 finished 状态,以便它可以从队列中移除。
KVO-兼容属性
NSOperationQueue 类是支持KVC和KVO的。我们可以根据需要观察以下属性:
@property(readonly, copy) NSArray<__kindof NSOperation *> *operations;
@property(readonly) NSUInteger operationCount;
@property NSInteger maxConcurrentOperationCount;
@property(getter=isSuspended) BOOL suspended;
@property(copy) NSString *name;
线程安全
使用单个 NSOperationQueue
对象是多线程安全的,不需要使用额外的锁来同步对该对象的访问
操作队列使用 Dispatch 框架来启动操作的执行。因此,操作总是在一个单独的线程上执行,而不管它们是被指定为同步还是异步。
主要方法
访问特定操作队列
-
@property(class, readonly, strong) NSOperationQueue *mainQueue;
返回与主线程相关联的操作队列
返回的队列在主线程中一次执行一个操作。主线程上的操作的执行与其他必须在主线程上执行的任务交替,例如服务事件和UI的更新。队列在runloop的 NSRunLoopCommonModes 模式下执行这些操作。队列的underlyingQueue属性的值是主线程的调度队列,该属性不能设置为其他值。
-
@property(class, readonly, strong) NSOperationQueue *currentQueue;
返回在当前线程运行的操作队列。
管理队列中的操作
-
- (void)addOperation:(NSOperation *)op;
添加指定操作到receiver。
一旦添加,该操作将会被保留在队列中,直到它完成执行
重点:
一个操作对象一次最多只能有一个操作队列,如果操作已经在其他队列中,调用该方法会抛出 NSInvalidArgumentException 异常。同样地,如果操作正在执行或者已经结束执行,该方法也会引发 NSInvalidArgumentException 异常
-
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;
添加指定的操作到队列中。
参数:
ops: 要添加到队列的操作
wait: 如果为YES,则阻塞当前线程,直到所有指定的操作都执行结束。如果为NO,操作将被添加到队列中,并直接返回给调用者。
操作对象一次最多只能在一个操作队列中,如果操作正在执行或者已经finished,则不能添加到队列中。对于ops参数中的任意操作,如果有任何错误条件,该方法都会抛出 NSInvalidArgumentException 异常。
一旦添加,operation将保留在队列中,直到operation的
finished
方法返回YES。 -
- (void)addOperationWithBlock:(void (^)(void))block;
将指定的block包装到operation 中,并将其添加到receiver中。
该方法首先将一个block封装到一个操作对象中,然后将其添加到receiver中。 不应尝试获取对新创建的操作对象的引用或确定其类型信息。
-
@property(readonly, copy) NSArray<__kindof NSOperation *> *operations;
当前在队列中的操作
该数组属性包含零个或多个 NSOperation 对象,按照添加到队列中的顺序。该数组不一定反映操作的执行顺序。可以使用该属性在任何给定的时刻访问排队的操作。直到操作完成它们的任务之前都会保留在队列中。因此,该数组可能包含正在执行或等待执行的操作。
可以使用KVO监测该属性值的更改。
-
@property(readonly) NSUInteger operationCount;
当前在队列中的操作数量。
由于队列中的操作数量随着这些操作的执行完成而发生变化,该属性的值反映了访问该属性时的瞬间的操作的数量。当使用该值时,操作的实际数量可能会有所不同。因此,不应该使用该值用于对象的枚举或其他精确计算。
可以使用KVO监测该属性的值。
-
- (void)cancelAllOperations;
取消所有排队或正在执行的操作
该方法调用当前队列中所有操作的cancel方法。
取消操作并不会自动从队列中移除操作,或者停止正在执行的操作。对于排队且等待执行的操作,
队列必须仍尝试执行该操作,然后才能确认该操作已被取消并将其移至finished状态。对于正在执行的操作,操作对象本身必须检查取消和停止正在执行的操作,以便于该操作可以移至finished状态。在这两种情况下,一个finished(或取消的)操作仍然有机会在从队列中移除之前执行它的 completion block。 -
- (void)waitUntilAllOperationsAreFinished;
阻塞当前线程,直到receiver的所有排队和正在执行的操作完成执行。
当调用该方法时,会阻塞当前线程,并等待receiver的当前和排队的operation完成其执行。当前线程被阻塞时,receiver将继续执行已经排队的操作并监测正在执行的操作。在此期间,当前线程不能添加操作到队列中,但其他线程可能会。一旦所有等待的操作都完成了,该方法会返回。
如果队列中没有操作,该方法将直接返回。
管理操作的执行
-
@property NSQualityOfService qualityOfService;
该属性指定了应用于添加到队列的操作对象的服务等级。如果操作对象具有一个明确的服务等级设置,则使用该值。该属性的默认值取决于创建队列的方式。对于自己创建的队列,默认值是
NSOperationQualityOfServiceBackground
。对于mainQueue
方法返回的队列,默认值是NSOperationQualityOfServiceUserInteractive,并且不能更改。服务级别会影响操作对象访问系统资源(比如CPU时间、网络资源、磁盘资源等)的优先级。具有更高质量的服务级别的操作比系统资源具有更高的优先级,以便它们可以更快速的执行任务。可以使用服务级别来确保用户显式请求的操作优先于其他不太重要的操作。
-
@property NSInteger maxConcurrentOperationCount;
可以同时执行的队列操作的最大数量。
此属性中的值仅影响当前队列同时执行的操作。 其他操作队列也可以并行执行其最大数量的操作。
减少并发操作的数量不会影响正在执行的任何操作。指定该属性值为
NSOperationQueueDefaultMaxConcurrentOperationCount
(推荐)时,系统会根据系统条件设置最大数量的操作。该属性的默认值是
NSOperationQueueDefaultMaxConcurrentOperationCount
。可以使用KVO监测该属性的值。NSOperationQueueDefaultMaxConcurrentOperationCount
这个数字是根据当前系统条件动态确定的。
暂停执行
-
@property(getter=isSuspended) BOOL suspended;
当该属性的值是NO时,队列会主动启动队列中准备执行的操作。将该属性设置为YES将阻止队列启动任何排队的操作。但已经执行的操作将继续执行。可以继续添加操作到暂停的队列中,但是这些操作在该属性变为NO之前都不会执行。
只有在完成执行时,操作才会从队列中移除。但是,为了完成执行,必须首先启动一个操作。因为一个暂停的操作不会开启任何新的操作,所以它不会移除当前排队且未执行的任何操作(包括取消的操作)
可以使用KVO监测该属性的值。该属性的默认值为NO
队列的配置
-
@property(copy) NSString *name;
操作队列的名称。提供了一种在运行时标识操作队列的方式。
该属性的默认值是包含操作队列内存地址的字符串。可以使用KVO监测该属性值的变化
-
@property(assign) dispatch_queue_t underlyingQueue;
用于执行操作的dispatch 队列
该属性的默认值为 nil。 可以将此属性的值设置为一个现有的调度队列,以使队列操作与提交到该调度队列的 block 相关联。
只有队列中没有操作时才应该设置该属性的值。当
operationCount
属性的值不等于0时设置该属性的值会引发NSInvalidArgumentException
异常。此属性的值不能是dispatch_get_main_queue
返回的值。注意:
如果 OS_OBJECT_IS_OBJC 为YES,该属性会自动retain 其分配的队列