一. 概念
NSOperation、NSOperationQueue 是苹果对GCD面向对象的封装,它的底层是基于GCD实现的,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。
二、对比GCD
- 提供可选实现的任务完成block。
- 支持任务之间添加依赖关系, 控制执行顺序。
- 支持设置任务的优先级, 可控制任务的相对执行顺序。
- 可使用KVO监听任务执行状态变化:isExecuteing、isFinished、isCancelled。
- 可以很方便的取消一个操作的执行。
- 设置最大并发数
三、任务 NSOperation
NSOperation 就是对任务进行的封装,封装好的任务交给不同的 NSOperationQueue 即可进行串行队列的执行或并发队列的执行。
NSOperation 是一个抽象类,并不能直接使用,必须使用它的子类,有三种方式:NSInvocationOperation、NSBlockOperation、自定义子类继承NSOperation,前两种是苹果为我们封装好的,可以直接使用,自定义子类,需要我们实现相应的方法。
1、使用子类 NSInvocationOperation
/*
创建一个NSInvocationOperation对象,指定执行的对象和方法
该方法可以接收一个参数即object
*/
NSInvocationOperation *invocationOpeartion = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOpeartion) object:nil];
// 如果没有添加到 NSOperationQueue, 则需要手动调用 start
[invocationOpeartion start];
/// 回调方法
- (void)invocationOpeartion {
for (int i = 0; i < 5; i++) {
NSLog(@"Task1 %@ %d", [NSThread currentThread], i);
}
}
// 打印: Task1 <NSThread: 0x6000019581c0>{number = 1, name = main} 0
注意: NSInvocationOperation 单独使用时, 并没有开启新的线程, 任务都是在当前线程中执行。只有添加到队列中才会开启新的线程。即默认情况下,如果操作没有放到队列中queue中,都是同步执行。只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作。
2、使用子类 NSBlockOperation
-
2.1 实例1:
//创建一个NSBlockOperation对象,传入一个block
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; I++)
{
NSLog(@"Task1 %@ %d", [NSThread currentThread], i);
}
}];
// 执行
[operation start];
// 打印: Task1 <NSThread: 0x6000019581c0>{number = 1, name = main} 0
NSBlockOperation 封装的操作数 == 1时,并没有开启新的线程, 任务都是在当前线程中执行。
-
2.2 实例2:
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; I++)
{
NSLog(@"task1=====%@ %d", [NSThread currentThread], i);
}
}];
[operation addExecutionBlock:^{
NSLog(@"task2=====%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"task3=====%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"task4=====%@",[NSThread currentThread]);
}];
[operation start];
/*
task3=====<NSThread: 0x600000509840>{number = 6, name = (null)}
task4=====<NSThread: 0x600000530200>{number = 3, name = (null)}
task1=====<NSThread: 0x600000558880>{number = 1, name = main} 0
task2=====<NSThread: 0x600000511680>{number = 5, name = (null)}
task1=====<NSThread: 0x600000558880>{number = 1, name = main} 1
task1=====<NSThread: 0x600000558880>{number = 1, name = main} 2
task1=====<NSThread: 0x600000558880>{number = 1, name = main} 3
task1=====<NSThread: 0x600000558880>{number = 1, name = main} 4
*/
NSBlockOperation 类有一个实例方法 addExecutionBlock,这个方法可以添加一个代码块,当执行 NSBlockOperation 对象 start 时,该对象将其所有块提交给默认优先级的并发调度队列。然后对象等待所有的块完成执行。当最后一个块完成执行时,操作对象将自己标记为已完成。由于块操作本身在单独的线程上运行, 所以应用程序的其他线程可以在等待块操作完成的同时继续工作。
需要说明的一点是,如果添加的任务较多的话,这些操作(包括 blockOperationWithBlock 中的操作)可能在不同的线程中并发执行,这是由系统决定的。
使用 addExecutionBlock 追加的任务是并发执行的,如果NSBlockOperation 封装的操作数 > 1,就会开启子线程,异步执行任务,这里追加的任务不一定就是子线程,也有可能是主线程。
-
2.3 设置完成回调
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码
NSLog(@"1");
}];
// 设置任务1完成的回调block
[operation1 setCompletionBlock:^{
NSLog(@"任务1完成了");
}];
3、使用自定义继承自NSOperation的子类
参考Apple官方文档:developer.apple.com/documentati…
3.1、先定义一个继承自NSOperation的子类,对于非并发操作,通常仅重写一个方法:重写main方法。
在此方法中,您可以放置执行给定任务所需的代码。当然,您还应该自定义初始化方法,以便更轻松地创建自定义类的实例。您可能还想定义 getter 和 setter 方法来访问操作中的数据。但是,如果您确实自定义 getter 和 setter 方法,则必须确保可以从多个线程安全地调用这些方法(加锁)。
@interface Operation : NSOperation
@end
@implementation Operation
-(void)main {
if (!self.isCancelled) {
for (int i = 0; i < 4; i++) {
sleep(2);
NSLog(@"%d==%@", i, NSThread.currentThread);
}
}
}
@end
// 使用:
Operation *op = [[Operation alloc] init];
[op start];
注意: 自定义的Operation并没有开启新的线程,任务的执行是在当前的线程中执行的。
3.2、先定义一个继承自NSOperation的子类,如果要创建并发操作,则至少需要重写以下方法和属性:
-
start() -
isAsynchronous -
isExecuting -
isFinished -
在并发操作中,您的start()方法负责以异步方式启动操作。无论您是生成线程还是调用异步函数,您都可以从此方法中执行。启动操作后,您的start()方法还应根据属性报告更新操作的执行状态。您可以通过发送关键路径的 KVO 通知来执行此操作,这会让感兴趣的客户端知道操作正在运行。--
isExecuting -
完成或取消任务后,并发操作对象必须为和关键路径生成 KVO 通知,以标记操作状态的最终变化。(在取消的情况下,即使操作未完全完成其任务,更新关键路径仍然很重要。排队操作必须报告它们已完成,然后才能从队列中删除。)除了生成 KVO 通知之外,您对和属性的覆盖还应继续根据操作状态报告准确的值。--
isFinished -
如果您自定义操作的依赖项功能,则可能必须重写其他方法并提供其他 KVO 通知。
@interface JDTestOperation()
@property(assign,nonatomic,getter= isExecuting)BOOLexecuting;
@property(assign,nonatomic,getter= isFinished)BOOLfinished;
@end
@implementation JDTestOperation
@synthesizeexecuting =_executing;
@synthesizefinished =_finished;
- (void)main
{
[self setExecuting:YES];
NSLog(@"%@ start",self.opName);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
//do something really important
sleep(1);
NSLog(@"%@ end",self.opName);
[self setExecuting:NO];
[self setFinished:YES];
});
}
- (void)setFinished:(BOOL)finished
{
[self willChangeValueForKey:@"isFinished"];
_finished= finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing
{
[self willChangeValueForKey:@"isExecuting"];
_executing= executing;
[self didChangeValueForKey:@"isExecuting"];
}
四、任务队列 NSOperationQueue
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作。
- 这里的队列是指操作队列,即用来存放操作的队列。不同于GCD的先进先出,
NSOperationQueue可以设置操作执行的优先级 - 操作队列通过设置最大并发操作数(
maxConcurrentOperationCount) 来控制并发、串行。 NSOperationQueue为我们提供了两种不同类型的队列:主队列和自定义队列,主队列运行在主线程之上,而自定义队列在后台执行。
1、NSOperationQueue 设置操作执行的优先级
NSOperation 优先级
NSOperation 提供了queuePriority(优先级)属性,queuePriority属性适用于同一操作队列中的操作,不适用于不同操作队列中的操作。
默认情况下,所有新创建的操作对象优先级都是NSOperationQueuePriorityNormal。但是我们可以通过setQueuePriority: 方法来改变当前操作在同一队列中的执行优先级。
// 优先级的取值
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
将操作添加到队列后操作会进入就绪状态,就绪状态的操作的执行顺序由操作之间的优先级决定
就绪状态的操作:简单理解就是准备就绪的操作就是没有需要依赖的操作,例如op2依赖于op1,那么op2就没有准备就绪,因为他需要等op1执行完才准备就绪
优先级不能取代依赖关系。如果要控制操作间的启动顺序,则必须使用依赖关系。
2、NSOperationQueue 设置最大并发操作数
使用NSOperation Queue创建的自定义队列同时具有串行、并发执行的能力,这里的关键就是maxConcurrentOperationCount这个关键属性,叫做最大并发操作数,用来控制一个特定队列中可以有多少个操作同时参与并发执行。
-
maxConcurrentOperationCount = 0,不执行。
-
maxConcurrentOperationCount 默认情况下为-1,表示不进行限制,可进行并发执行,在子线程中执行任务。
-
maxConcurrentOperationCount 为1时,队列为串行队列,按顺序串行执行,并且一个操作完成之后,下一个操作才开始执行。
-
maxConcurrentOperationCount 大于1时,队列为并发队列,在子线程中操作并发执行,而开启线程数量是由系统决定的。
-
maxConcurrentOperationCount值并不是表示并发执行的线程数量,而是在一个队列中能够同时执行的任务的数量。
/**
* 设置 MaxConcurrentOperationCount(最大并发操作数)
*/
- (void)setMaxConcurrentOperationCount {
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.设置最大并发操作数
queue.maxConcurrentOperationCount = 1; // 串行队列
// queue.maxConcurrentOperationCount = 2; // 并发队列
// queue.maxConcurrentOperationCount = -1; // 不进行限制
// 3.添加操作
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
NSLog(@"4---%@", [NSThread currentThread]); // 打印当前线程
}
}];
}
3、NSOperationQueue 提供了主队列和自定义队列
- 主队列:
[NSOperationQueue mainQueue];, 凡是添加到主队列的NSOperation任务都会放到主线程执行。 - 自定义队列:
[[NSOperationQueue alloc] init];, 凡是添加到自定义队列的NSOperation任务都会放到子线程执行。
3.1 添加任务到队列: 将操作加入到操作队列后能够开启新线程,进行并发执行。
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 方式1:
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
[queue addOperation:blockOperation];
// 方式2:
[queue addOperationWithBlock:^{
// 任务代码
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
注意: 不需要调用 NSBlockOperation 的 start方法
3.2 添加多个操作到队列: 将操作加入到操作队列后能够开启新线程,进行并发执行。
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 操作1
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
// 操作2
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
// 方式1:
[queue addOperation:blockOperation];
[queue addOperation:invocationOperation];
// 方式2:
// waitUntilFinished参数,如果传YES,则表示会等待队列里面的任务执行完成后才会往下执行,也就是会阻塞线程
[queue addOperations:@[blockOperation, invocationOperation] waitUntilFinished:true];
/// 回调方法
- (void)invocationOperation {
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}
提示: waitUntilFinished参数,如果传YES,则表示会等待队列里面的任务执行完成后才会往下执行,也就是会阻塞线程
4、NSOperation 操作依赖
NSOperation、NSOperationQueue 最吸引人的地方是它能添加操作之间的依赖关系。通过操作依赖,我们可以很方便的控制操作之间的执行先后顺序。NSOperation 提供了3个接口供我们管理和查看依赖。
- (void)addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作 op 的完成。
- (void)removeDependency:(NSOperation *)op; 移除依赖,取消当前操作对操作 op 的依赖。
@property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组。
我们现在来假设一个场景,有两个操作A、B,A操作完B才能操作,也就是B依赖A
/**
* 操作依赖
* 使用方法:addDependency:
*/
- (void)addDependency {
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.创建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
}];
// 3.添加依赖
[op2 addDependency:op1]; // 让op2 依赖于 op1,则先执行op1,在执行op2
// 4.添加操作到队列中
[queue addOperation:op2];
[queue addOperation:op1];
}
可以看到即使两个操作在不同线程执行也就是并发执行,也是op1先执行才能执行op2
提示:
queuePriority不能取代依赖关系
queuePriority属性只对同一个队列有效
5、NSOperation、NSOperationQueue 线程间的通信
子线程完成任务, 会主线程更新UI:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码 -- 异步进行耗时操作
for (int i = 0; i < 5; i++) {
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
// 回主队列更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"更新UI");
}];
}];
[queue addOperation:blockOperation];
6、NSOperation、NSOperationQueue 线程安全
6.1 NSOperation、NSOperationQueue 非线程安全
下面,我们模拟火车票售卖的方式,实现 NSOperation 线程安全和解决线程同步问题。 场景:总共有100张火车票,有两个售卖火车票的窗口,一个是广东火车票售卖窗口,另一个是广西火车票售卖窗口。两个窗口同时售卖火车票,卖完为止。
/**
* 非线程安全:不使用 NSLock
* 初始化火车票数量、卖票窗口(非线程安全)、并开始卖票
*/
- (void)initTicketStatusNotSave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
self.ticketSurplusCount = 50;
// 1.创建 queue1,queue1 代表广东火车票售卖窗口
NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
queue1.maxConcurrentOperationCount = 1;
// 2.创建 queue2,queue2 代表广西火车票售卖窗口
NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
queue2.maxConcurrentOperationCount = 1;
// 3.创建卖票操作 op1
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[self saleTicketNotSafe];
}];
// 4.创建卖票操作 op2
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[self saleTicketNotSafe];
}];
// 5.添加操作,开始卖票
[queue1 addOperation:op1];
[queue2 addOperation:op2];
}
/**
* 售卖火车票(非线程安全)
*/
- (void)saleTicketNotSafe {
while (1) {
if (self.ticketSurplusCount > 0) {
//如果还有票,继续售卖
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else {
NSLog(@"所有火车票均已售完");
break;
}
}
}
6.2 线程安全,加线程锁(使读写操作在同一线程操作,才能保证线程安全。atomic只是保证读写安全)
- (void)saleTicketNotSafe {
while (1) {
[self.lock lock];
if (self.ticketSurplusCount > 0) {
//如果还有票,继续售卖
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else {
NSLog(@"所有火车票均已售完");
break;
}
[self.lock unlock];
}
}
7、NSOperation、NSOperationQueue 常用属性和方法归纳
NSOperation 常用属性和方法
属性:
注意:
1.取消任务和暂停任务一样, 不会取消当前正在执行的任务, 只能取消还未执行的任务
2.如果在任务执行的过程中暂停队列中的任务, 那么当前正在执行的任务并不会被暂停, 而是会暂停队列中的下一个任务
3.恢复任务, 是从队列第一个没有被执行过的任务开始恢复
/** 是否已经取消线程 */
@property (readonly, getter=isCancelled) BOOL cancelled;
/** 是否正在执行 */
@property (readonly, getter=isExecuting) BOOL executing;
/** 是否已经完成 */
@property (readonly, getter=isFinished) BOOL finished;
/** 是否并行操作 */
@property (readonly, getter=isConcurrent) BOOL concurrent;
/** 是否异步操作 */
@property (readonly, getter=isAsynchronous) BOOL asynchronous NS_AVAILABLE(10_8, 7_0);
/** 是否能准备运行,这个值和任务的依赖关系相关 */
@property (readonly, getter=isReady) BOOL ready;
/** 得到所有依赖的NSOperation任务 */
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
/** 该操作的优先级 */
@property NSOperationQueuePriority queuePriority;
/** 操作优先级 */
/*
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
*/
/** NSOperation执行完毕后调用 */
@property (nullable, copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);
方法:
/** 开始方法.在当前任务状态和依赖关系合适的情况下,启动NSOperation的main方法任务,需要注意缺省实现只是在当前线程运行。如果需要并发执行,子类必须重写这个方法,并且使 - (BOOL)isConcurrent 方法返回YES */
- (void)start;
/** 主方法 start执行后,主类应该重写此方法相比与start方法 */
- (void)main;
/** 取消 */
- (void)cancel;
/** 添加一个依赖 */ //依赖只是设置先后执行的顺序关系,可以跨队列依赖,不可以循环依赖。添加依赖以后会顺序执行任务,但是不一定开一个线程,可能会开多个线程,但是不会太多
- (void)addDependency:(NSOperation *)op;
/** 删除一个依赖 依赖的任务关系不会自动消除,必须调用该方法 */
- (void)removeDependency:(NSOperation *)op;
/** 阻塞当前线程,直到该NSOperation结束。可用于线程执行顺序的同步 */
- (void)waitUntilFinished //
取消操作方法
- (void)cancel; 可取消操作,实质是标记 isCancelled 状态。
判断操作状态方法
- (BOOL)isFinished; 判断操作是否已经结束。
- (BOOL)isCancelled; 判断操作是否已经标记为取消。
- (BOOL)isExecuting; 判断操作是否正在在运行。
- (BOOL)isReady; 判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。
操作同步
-(void)waitUntilFinished; 阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
- (void)setCompletionBlock:(void (^)(void))block; completionBlock 会在当前操作执行完毕时执行 completionBlock。
- (void)addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作 op 的完成。
- (void)removeDependency:(NSOperation *)op;移除依赖,取消当前操作对操作 op 的依赖。
@property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组。
NSOperationQueue 常用属性和方法
属性:
/** 返回已加入执行operation的数组,当某个operation结束后会自动从这个数组清除 */
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
/** 当前队列的操作数量 */
@property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);
/** 最大并发量 (默认是-1,最大为6) */
@property NSInteger maxConcurrentOperationCount;
/** 暂停任务和开始任务,挂起的队列不会影响已执行和执行完的任务,队列暂停再添加任务也不会自动执行任务了 */
@property (getter=isSuspended) BOOL suspended;
/** 线程队列的名字 */
@property (nullable, copy) NSString *name NS_AVAILABLE(10_6, 4_0);
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);
@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue NS_AVAILABLE(10_10, 8_0);
方法:
/** 添加操作 */
- (void)addOperation:(NSOperation *)op;
/** 批量参加操作 wait标志是否当前线程等待所有operation结束后,才返回 */
- (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的执行,实质是调用各个operation的cancel方法 */
- (void)cancelAllOperations;
/** 当前线程等待,直到opA和opB都执行结束 opA opB都是 添加到队列中的NSOperation */
- (void)waitUntilAllOperationsAreFinished;
#if FOUNDATION_SWIFT_SDK_EPOCH_AT_LEAST(8)
/** 返回当前NSOperationQueue,如果当前线程不是在NSOperationQueue上运行则返回nil */
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue NS_AVAILABLE(10_6, 4_0);
/** 返回主线程的NSOperationQueue,缺省总是有一个queue */
@property (class, readonly, strong) NSOperationQueue *mainQueue NS_AVAILABLE(10_6, 4_0);
#endif
取消/暂停/恢复操作
- (void)cancelAllOperations; 可以取消队列的所有操作。
- (BOOL)isSuspended; 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
- (void)setSuspended:(BOOL)b; 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。
操作同步:
- (void)waitUntilAllOperationsAreFinished; 阻塞当前线程,直到队列中的操作全部执行完毕。
添加/获取操作:
- (void)addOperationWithBlock:(void (^)(void))block; 向队列中添加一个 NSBlockOperation
类型操作对象。
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
- (NSArray *)operations; 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
- (NSUInteger)operationCount; 当前队列中的操作数。
**获取队列: **
+ (id)currentQueue; 获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil。
+ (id)mainQueue; 获取主队列。