- NSOperation 简介
- NSOperation 使用
- NSOperation 属性 和 方法
1.1 属性
1.2 方法 - NSOperation 子类
2.1 NSInvocationOperation
属性
方法
栗子
2.2 NSBlockOperation
属性
方法
栗子 - 自定义NSOperation
使用
- NSOperation 属性 和 方法
- NSOperationQueue
3.1 属性和方法
3.2 创建队列
3.3 NSOperatinoQueue 串行执行和并行执行 最大并发数
3.4 操作依赖
3.5 线程通信
1. NSOperation 简介
NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性也更高。
是OC语言中基于GCD(GCD详解)的面向对象的封装,具有以下特性
- 使用起来比GCD更加简单(面向对象);
- 提供了一些用GCD不好实现的功能,比如可以取消在任务处理队列中的任务,添加任务间的依赖关系等等;
- 苹果推荐使用,使用NSOperation不用关心线程以及线程的生命周期;
- 可以指定操作之间的依赖关系,是将操作添加到队列。
- 并发队列,异步执行(多个线程,无序执行)。
2. NSOperation 使用
NSOperation是个抽象类,并不能封装任务。我们只有使用它的子类来封装任务,先说下 NSOperation类的属性和方法
1. NSOperation 属性和方法
1.1 属性:
注意:
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);
1.2 方法:
/** 开始方法.在当前任务状态和依赖关系合适的情况下,启动NSOperation的main方法任务,需要注意缺省实现只是在当前线程运行。如果需要并发执行,子类必须重写这个方法,并且使 - (BOOL)isConcurrent 方法返回YES */
- (void)start;
/** 主方法 start执行后,主类应该重写此方法相比与start方法 */
- (void)main;
/** 取消 */
- (void)cancel;
/** 添加一个依赖 */ //依赖只是设置先后执行的顺序关系,可以跨队列依赖,不可以循环依赖。添加依赖以后会顺序执行任务,但是不一定开一个线程,可能会开多个线程,但是不会太多
- (void)addDependency:(NSOperation *)op;
/** 删除一个依赖 依赖的任务关系不会自动消除,必须调用该方法 */
- (void)removeDependency:(NSOperation *)op;
/** 阻塞当前线程,直到该NSOperation结束。可用于线程执行顺序的同步 */
- (void)waitUntilFinished //
2. NSOperation 子类
2.1 NSInvocationOperation
属性:
/** 队列的invocation参数 (只读) */
@property (readonly, retain) NSInvocation *invocation;
/** 结果(只读) */
@property (nullable, readonly, retain) id result;
方法:
/** 通过SEL实例方法 */
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
/** 通过NSInvocation 实例方法 */
- (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;
//注意: 1. 调用方法可以使用start也可以放到队列中(NSOperationQueue),但是两者不可同时使用.
// 2. 调用start 开启任务,会在当前线程开启任务,如果几个操作一起调用start 则在当前线程串行完成任务
// 3. 把操作放到队列queue中开启任务会在其他线程去执行任务
栗子:
//创建
NSInvocationOperation *skyOpA = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(skyRun:) object:nil];
//开始执行操作(这样是在主线程中,只有加到NSOperationQueue才能并发执行)
[skyOpA start];
- (void)skyRun:(NSOperation*)operation
{
NSLog(@"skyOpA = %@ ",[NSThread currentThread]);
}
2.2 NSBlockOperation
属性:
/** 执行的所有blocks */
@property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;
实例方法和类方法:
/** 添加blocks 没有参数没有返回值 */
- (void)addExecutionBlock:(void (^)(void))block;
/** 通过block初始化操作,该block没有参数没有返回值 */
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
栗子:
NSBlockOperation *skyOpB = [NSBlockOperation blockOperationWithBlock:^{
//在主线程
NSLog(@"skyOpB 1 = %@",[NSThread currentThread]);
}];
//添加操作 以下都在多线程中~
[skyOpB addExecutionBlock:^{
NSLog(@"skyOpB 2 = %@",[NSThread currentThread]);
}];
[skyOpB addExecutionBlock:^{
NSLog(@"skyOpB 3 = %@",[NSThread currentThread]);
}];
[skyOpB addExecutionBlock:^{
NSLog(@"skyOpB 4 = %@",[NSThread currentThread]);
}];
// 如果只封装了一个操作, 那么默认会在主线程中执行
// 如果封装了多个操作, 那么除了第一个操作以外, 其它的操作会在子线程中执行
[skyOpB start];
2.3 自定义 ** NSOperation**
先定义一个继承自NSOperation的子类,重写main方法
栗子:
#import <Foundation/Foundation.h>
@interface SkyOperation : NSOperation
@end
#import "SkyOperation.h"
@interface SkyOperation()
@property(assign,nonatomic,getter= isExecuting)BOOL executing;
@property(assign,nonatomic,getter= isFinished)BOOL finished;
@end
@implementation SkyOperation
@synthesize executing = _executing;
@synthesize finished = _finished;
- (void)main
{
NSLog(@" skyOp = %@ ",[NSThread currentThread]);
// @autoreleasepool {
// }
}
- (void)setFinished:(BOOL)finished
{
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing
{
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
@end
使用自定义的NSOperation
SkyOperation *operation = [[SkyOperation alloc]init];
[operation start];
注意:
- 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
- 经常通过-(BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
- 关于NSOperation的子类在NSOperationQueue中执行完无法释放的问题你的NSOperation dealloc了么?并发数真的生效了么?
3. NSOperationQueue
将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
3.1 属性和方法
属性:
/** 返回已加入执行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
3.2 创建队列
- (void)addOperationToQueue
{
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//添加到队列(自动调用start)
[queue addOperationWithBlock:^{
NSLog(@"queue 1 %@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"queue 2 %@",[NSThread currentThread]);
}];
//NSInvocationOperation
NSInvocationOperation *skyOpA = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(skyRun:) object:nil];
//NSBlockOperation
NSBlockOperation *skyOpB = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"skyOpB 1 = %@",[NSThread currentThread]);
}];
//SkyOperation
SkyOperation *operation = [[SkyOperation alloc]init];
[queue addOperation:skyOpA];//自动调用start
[queue addOperation:skyOpB];//自动调用start
[queue addOperation:operation];
}
- (void)skyRun:(NSOperation*)operation
{
NSLog(@"skyOpA = %@ ",[NSThread currentThread]);
}
3.3 NSOperatinoQueue 串行执行和并行执行 最大并发数
@property NSInteger maxConcurrentOperationCount;
- maxConcurrentOperationCount 默认等于 -1, 代表不限制, 可以创建N多线程
- alloc/init的NSOperatinoQueue队列默认就是并发, 如果想实现串行, 那么就设置maxConcurrentOperationCount = 1
- 注意: 最大并发数, 不能设置为0, 否则任务不会被执行
// queue.maxConcurrentOperationCount = 2; //最大并发数
queue.maxConcurrentOperationCount = 1; // 就变成了串行队列
3.4 操作依赖
操作A执行完后,才能执行操作B. 注意: 不能相互依赖
//操作依赖
- (void)addDependency
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *opA = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"A-----%@", [NSThread currentThread]);
}];
NSBlockOperation *opB = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"B-----%@", [NSThread currentThread]);
}];
[opB addDependency:opA]; // 让opB 依赖于 opA, 执行完opA再执行opB
[queue addOperation:opA];
[queue addOperation:opB];
}
3.5 线程通信
开启线程下载图片,下载完成之后主线程显示图片
//开启线程
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
//异步下载图片
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
//主线程显示图片
}];
}];