NSOperationQueue
NSOperationQueue是管理一组操作(operation)的队列。队列中操作是根据操作的优先级(queuePriority)和操作的依赖关系(Dependency)来决定执行的先后顺序。操作一旦加入到队列中,该操作会一直存在这个队列中,不能直接从队列中删除,除非改操作被显示的取消( 调用cancel() )或者完成才能移除。
注意:取消操作会导致操作忽略它可能拥有的任何依赖关系,例如:operationA依赖operationB,并且operationB还没执行,此时你想取消operationA,这个时候operationA不会等operationB完成后在执行取消操作,而是直接执行start方法将operationA标记为完成状态,然后从队列中删除。
队列通常会提供运行操作的线程,使用libdispatch库(中央调度)启动它们的操作。操作不管是异步或者同步,都是在单独的线程上执行,所以可以安全的使用多个线程中的单个NSOperationQueue对象,不需要创建额外的锁来同步该对象的访问。
让我们看看NSOperationQueue的属性和方法:
//操作数组,只读
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
//包含操作的数量,只读
@property (readonly) NSUInteger operationCount ;
//队列中同时可执行的最大操作数量
@property NSInteger maxConcurrentOperationCount;
//是否暂停执行操作
@property (getter=isSuspended) BOOL suspended;
// 队列的名字
@property (nullable, copy) NSString *name ;
//系统管理的队列的资源分配
@property NSQualityOfService qualityOfService;
//依赖的现有队列,默认为nil
@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue;
//当前的操作队列
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue
//当前主线程关联的队列
@property (class, readonly, strong) NSOperationQueue *mainQueue
//添加操作对象
- (void)addOperation:(NSOperation *)op;
//添加操作对象组 waitUntilFinished:是否阻塞当前线程,等所有操作都完成
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;
//添加操作任务
- (void)addOperationWithBlock:(void (^)(void))block;
//取消所有的操作
- (void)cancelAllOperations;
//阻塞线程知道所有操作完成
- (void)waitUntilAllOperationsAreFinished;
代码实现一下:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"OperationQueueTest";
NSLog(@"%@",queue.name);
queue.maxConcurrentOperationCount = 2;
NSOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operationA:%@", [NSThread currentThread]);
//为了检验maxConcurrentOperationCount属性
sleep(5);
}];
NSOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operationB:%@", [NSThread currentThread]);
sleep(5);
}];
NSOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operationC:%@", [NSThread currentThread]);
}];
NSOperation *operationD = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operationD:%@", [NSThread currentThread]);
}];
NSOperation *operationE = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operationE:%@", [NSThread currentThread]);
}];
//operationD依赖于A,B,C,所以必须等ABC完成后才能执行D
[operationD addDependency:operationA];
[operationD addDependency:operationB];
[operationD addDependency:operationC];
[queue addOperation:operationA];
[queue addOperation:operationB];
[queue addOperation:operationC];
[queue addOperation:operationD];
[queue addOperation:operationE];
从下面的输出可以看到,运行后可以看到线程的最大并发数为2,D在ABC完成后才执行。
不过这里有个小问题,为什么线程都是从number = 3开始的,搜了一些资料,也尝试了一下,只知道number = 1 为主线程,那么number = 2 是什么?如果有读者知道希望告诉我一下,不胜感激!
NSOperation
NSOperation是表示与单个任务关联的代码和数据的抽象类,所以不能直接使用,需要使用系统定义的子类(NSInvocationOperation和NSBlockOperation)来执行实际的任务。虽然NSOperation是抽象的,但是它的基本实现包含了重要的逻辑来协调任务的安全执行。
如果你不想使用NSOperationQueue,可以直接调用NSOperation的start() 方法来执行操作,只能二选一。手动执行操作会增加代码的负担,因为启动不处于就绪状态的操作会触发异常。
//开始操作
- (void)start;
//操作任务的入口,一般用于自定义的NSOperation的子类
- (void)main;
//操作是否取消,默认为NO
@property (readonly, getter=isCancelled) BOOL cancelled;
//取消操作
- (void)cancel;
//操作是否执行
@property (readonly, getter=isExecuting) BOOL executing;
//操作是否完成
@property (readonly, getter=isFinished) BOOL finished;
//操作是否异步执行其他任务
@property (readonly, getter=isConcurrent) BOOL concurrent;
//操作是否异步执行任务
@property (readonly, getter=isAsynchronous) BOOL asynchronous API_AVAILABLE(macos(10.8), ios(7.0), watchos(2.0), tvos(9.0));
//操作是否准备被执行
@property (readonly, getter=isReady) BOOL ready;
//添加依赖
- (void)addDependency:(NSOperation *)op;
//移除依赖
- (void)removeDependency:(NSOperation *)op;
//操作依赖的的操作组
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
//操作在队列的优先级
@property NSOperationQueuePriority queuePriority;
//操作的主要任务发出后的回调
@property (nullable, copy) void (^completionBlock)(void) API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
//操作完成前是否阻塞当前线程的执行
- (void)waitUntilFinished API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
//操作的线程优先级(弃用)
@property double threadPriority API_DEPRECATED("Not supported", macos(10.6,10.10), ios(4.0,8.0), watchos(2.0,2.0), tvos(9.0,9.0));
//系统资源的分配
@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
//操作名字
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
NSBlockOperation
NSBlockOperation是NSOperation的子类,用来管理一个或者多个block的并发执行的操作。一个blockOperation可以执行一个或多个block,当执行多个block的时候,要等所有block都执行完成后,才考虑operation本身。
//创建一个带有block的opertation对象。
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
//添加执行的block
- (void)addExecutionBlock:(void (^)(void))block;
//opertaion中含有的所有执行block
@property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;
// 创建
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{}];
// 操作完成的时候回调
[operationA setCompletionBlock:^{}];
// 添加任务
[operation addExecutionBlock:^{}];
// 开始 异步并发:第一个任务在当前线程,其他任务在其他线程
[operation start];
// 添加到队列 所有任务都在同一个队列中
[queue addOperation:operation1];
// 设置依赖 A->B->C
[operationB addDependency:operationA];
[operationC addDependency:operationB];
NSInvocationOperation
NSInvocationOperation是NSOperation的子类,管理那些指定为调用的封装好的单个任务的执行。用来发起一个操作,这个操作能调用指定对象上的方法。
//创建一个调用特定对象的方法的invocationOperation
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
//根据一个NSInvocation对象 创建一个InvocationOpetation
- (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;
// 包含的invocation对象
@property (readonly, retain) NSInvocation *invocation;
//调用方法或者invocation的结果
@property (nullable, readonly, retain) id result;
// 创建
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
// 开始 同步执行(在当前线程执行操作)
[operation start];
// 添加操作到队列中,会自动异步执行
[queue addOperation:operation];