简介概念了解
在SDWebImage中有见过NSOperation这个函数
关于NSOperation我们可以看到官方文档中的解释: 表示于单个任务相关联的代码和数据的抽象类 因为其是一个抽象类,我们不能直接使用它,而是使用它的子类或使用系统定义的子类(NSInvocationOperation或NSBlockOperation)来执行实际的任务。尽管是抽象的,NSOperation的基本实现确实包含了重要的逻辑来协调任务的安全执行。这种内置逻辑的存在允许您将注意力集中在任务的实际实现上,而不是确保它与其他系统对象正确公所所需的粘合代码上。
通常通过将操作添加到操作队列(NSOperationQueue类的实例)来执行操作。
如果不想使用操作队列,可以直接从代码中调用操作的start方法来执行操作。手动执行操作会给代码带来更大的负担,因为启动未处于就绪状态的操作会触发异常。
操作队列是OC中的一种高级的并发处理方法,现在它是基于GCD更高一层的封装,完全面向对象。但是比GCD更加简单易用、代码可读性也更高。
操作队列不同于GCD中FIFO的原则。NSOperationQueue对于添加到队列中的操作,首先进入准备就绪的状态(就绪状态取决于操作之间的依赖关系),然后进入就绪状态的操作开始执行顺序由操作之间的优先级决定。
操作队列也为我们提供了两种不同类型的队列:主队列和自定队列,主队列运行在主线程之上,而自定义队列在后台执行
为什么我们需要使用NSOperation、NSOperationQueue
- 可以添加完成的代码块,在操作完成之后执行
- 添加操作之间的依赖关系,方便的控制执行顺序
- 设定操作执行的优先级
- 可以很方便的取消一个操作的执行
- 使用KVO观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled
基本使用
NSOperation 单独使用时系统同步执行操作,配合 NSOperationQueue 我们能更好的实现异步执行
使用步骤分为三步
- 创建操作:将需要执行的操作封装到一个NSOperation对象中
- 创建队列:创建NSOperationQueue对象
- 将操作加入到队列中:将NSOperation对象添加到NSOperationQueue对象中
之后,系统将队列中的操作取出来执行操作
创建操作
上面说了,我们不能直接使用抽象类,而是使用其子类或系统定义的子类
使用子类NSInvocationOperation(Invocation:调用)
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
[op start];
- (void)task1 {
NSLog(@"1---%@", [NSThread currentThread]);
}
 可以看到,操作实在当前线程执行的,没有开启新线程
我们使用NSThread创建线程后自启动新线程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
在新线程执行操作,结果就是其他线程 也就意味着其只能在自己当前所在的线程中执行同步操作
使用子类NSBlockOperation
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
}];
[op start];
一样 也是在当前线程执行同步操作,在其他线程操作则为其他线程
但是其提供了一个方法 addExecutionBlock:^ 可以为NSBlockOperation添加额外的操作 这些操作可以在不同的线程并发执行 
使用自定义继承自NSOperation的子类
如果使用系统提供的两个子类不能满足日常需求,我们可以使用自定义继承自NSOperation的子类。可以通过重写main或者start方法来定义自己的NSOperation对象
重写main方法
NSOperationTest *op = [[NSOperationTest alloc] init];
[op start];
自定义的Test类中重写main方法
- (void)main {
if (!self.isCancelled) {
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
}
}

重写start方法
- (void)start {
if (!self.isCancelled) {
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
}
[super start];
}
一个道理
创建队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
- 自定义队列:自动放到子线程中执行,同时包含了串行、并发的功能?(什么串行???)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
将操作加入到队列中
两种方法
-
addOperation :将操作加入队列中能够开启新线程,进行并发执行两个任务   -
addOperationWithBlock: 将操作加入到操作队列后能够开启新线程,进行并发执行 
NSOperationQueue控制串行、并发
NSOperationQueue 有个属性maxConcurrentOperationCount ,最大并发数 用来控制一个队列中可以有多少个操作同时参与并发
- 关于最大并发数
maxConcurrentOperationCount maxConcurrentOperationCount 默认为-1,表示可以并发执行maxConcurrentOperationCount 为1时,串行队列,只能串行执行maxConcurrentOperationCount 大于1,队列为并发队列,



NSOperation操作依赖
我们可以添加操作之间的依赖关系,通过操作依赖,我们可以很方便的控制操作之间的操作先后顺序
addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作op的完成removeDependency:(NSOperation *)op; 移除依赖,取消当前操作对操作op的依赖@property (readonly, copy) NSArray <NSOperation *> * dependencies 在当前操作开始执行之前完成执行的所有操作对象数组 
使用addOperations
@property (readonly, copy) NSArray <NSOperation *> * dependencies;
waitUntilFinished为YES 数组中的两个线程并发进行 第三个等待前两个结束之后执行 并且第三个使用的是数组中的最后一个线程  为NO 不等待 三个新线程 
NSOperation优先级
NSOperation提供了优先级属性(queuePriority )。 我们可以通过setQueuePriority 方法来改变当前操作在统一队列中的执行优先级
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
}
前面提到过,对于添加到队列中的操作,首先进入准备就绪的状态,就绪状态取决于操作之间的依赖关系,然后进入就绪状态的操作的开始执行顺序由操作之间的优先级确定(优先级是操作对象自身的属性)
优先级不能取代依赖关系。这也就意味着
假如一个队列中既包含了准备就绪状态的操作(没有任何依赖关系,其就是第一个),又包含了未准备就绪的操作,未准备就绪的操作优先级比准备就绪的操作优先级高,那么虽然准备就绪的操作优先级低,也会优先执行。
也就是官方文档中的:
仅当需要对非相关操作的相对优先级进行分类时,才应使用优先级值。优先级值不应用于实现不同操作对象之间的依赖关系管理
如果优先级一样 就按添加顺序执行(最大线程数为1)  为什么第一个优先级设置为Low没用,为什么第一个固定与addOperation添加顺序的第一个有关呢 说好的不看添加顺序呢?
NSOperation、NSOperationQueue线程间的通讯
回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
}];
线程安全
NSLock来加锁
- 初始化NSLock对象
self.lock = [[NSLock alloc] init] - 上锁
[self.lock lock] - 解锁
[self.lock unlock] 
常用属性和方法
NSOperation
- 取消操作
cancel - 判断操作状态方法
-
-
-
-
- 操作同步
-
-
setCompletionBlock:(void (^)(void))block; 当前操作完毕之后执行block -
-
-
@property (readonly, copy) NSArray<NSOperation *> *dependencies; 数组存储操作
NSOperationQueue
- 取消/暂停/恢复队列中的操作
-
cancelAllOperations; 取消队列中所有的操作 -
isSuspended 判断队列是否处理暂停状态 YES:暂停状态,NO恢复状态 -
setSuspended:(BOOL)b; 设置操作的暂停和恢复 YES:暂停,NO:恢复 - 同步操作
-
waitUntilAllOperationsAreFinished; 阻塞当前线程,直到队列中的操作全部完成 - 添加/获取
-
addOperationWithBlock:(void (^)(void))block 向队列中添加一个NSBlockOperation类型的操作对象 -
addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; 添加操作数组,wait标志是否阻塞当前线程知道所有操作结束 -
-
- 获取队列
-
currentQueue 当前队列,如果当前线程不在Queue上,返回nil -
|