祭出demo
Foundation
框架提供了NSOperation和NSOpeartionQueue 这两个面向对象的多线程类,这两个类与GCD
的功能类似。NSOperation
是对任务的封装,NSOperationQueue
提供执行队列,自动实现多核并行计算,自动管理线程的生命周期。并发情况下也是底层提供线程池模型来管理。但是NSOperation
提供了更多的可定制开发。
使用NSOperation
和NSOperationQueue
来编写多线程程序非常简单,只需要创建一个任务对象,创建一个执行队列或者像获取主线程一样获取一个主任务队列,然后将任务提交给队列即可实现并发,如过你想要串行只需要将队列的并发数设置为1
即可。接下来将分别介绍两个类的使用。
1、NSOperation
任务的封装
这里的任务就是NSOperation
类的一个方法,main
方法或start
方法,但NSOperation
类的这两个方法是空方法,没有干任何事情,因此,我们需要自定义继承NSOperation
类并重写相关方法,OC
也提供了两个子类供我们使用NSBlockOperation
和NSInvocationOperation
。
接下来看一下NSOperation
类中比较重要的属性和方法:
/*
对于并发Operation需要重写该方法
也可以不把operation加入到队列中,手动触发执行,与调用普通方法一样
*/
- (void)start;
/*
非并发Operation需要重写该方法
*/
- (void)main;
//只读属性任务是否取消,如果自定义子类,需要重写该属性
@property (readonly, getter=isCancelled) BOOL cancelled;
/*
设置cancelled属性为YES
仅仅标记cancelled属性,不退出任务,和NSThread的cancel一个机制
自定义子类时需要使用该属性判断是否在外部触发了取消任务的操作,手动退出任务
*/
- (void)cancel;
//只读属性,任务是否正在执行,如果自定义子类,需要重写该属性
@property (readonly, getter=isExecuting) BOOL executing;
/*
只读属性,任务是否结束,如果自定义子类,需要重写该方法
对于加入到队列的任务来说,当finished设置为YES后,队列会将任务移除出队列
*/
@property (readonly, getter=isFinished) BOOL finished;
//是否为并发任务,该属性已经被标识即将弃用,应该使用下面的asynchronous属性
@property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below
/*
只读属性,判断任务是否为并发任务,默认返回NO
如果需要自定义并发任务子类,需要重写getter方法返回YES
*/
@property (readonly, getter=isAsynchronous) BOOL asynchronous;
/*
只读属性,任务是否准备就绪
对于加入队列的任务,当ready为YES,标识该任务即将开始执行
如果任务有依赖的任务没有执行完成ready为NO
*/
@property (readonly, getter=isReady) BOOL ready;
/*
添加一个NSOperation为当前任务的依赖
如果一个任务有依赖,需要等待依赖的任务执行完成才能开始执行
*/
- (void)addDependency:(NSOperation *)op;
//删除一个依赖
- (void)removeDependency:(NSOperation *)op;
//任务在队列里的优先级
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
//任务在队列里的优先级
@property NSOperationQueuePriority queuePriority;
/*
任务完成后的回调方法
当finished属性设置为YES时才会执行该回调
*/
@property (nullable, copy) void (^completionBlock)(void);
上述内容中有一些属性和方法是在自定义NSOperation
子类中必须要重写的,自定义子类能够提供更高的可定制性,因此,编写自定义子类更复杂。
OC
为我们提供了两个子类NSBlockOperation
和NSInvocationOperation
,这两个子类已经帮我们完成了各种属性的设置操作,我们只需要编写一个任务的block
或者一个方法即可像使用GCD
一样方便的编写多线程程序。
接下来举两个创建任务的例子:
//创建一个NSBlockOperation对象,传入一个block
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 100; i++)
{
NSLog(@"Task1 %@ %d", [NSThread currentThread], i);
}
}];
/*
创建一个NSInvocationOperation对象,指定执行的对象和方法
该方法可以接收一个参数即object
*/
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task:) object:@"Hello, World!"];
2、NSOperationQueue
执行队列
看一下该类中有哪些比较重要的属性和方法:
//向队列中添加一个任务
- (void)addOperation:(NSOperation *)op;
/*
向队列中添加一组任务
是否等待任务完成,如果YES,则阻塞当前线程直到所有任务完成
如果为False,不阻塞当前线程
*/
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;
//向队列中添加一个任务,任务以block的形式传入,使用更方便
- (void)addOperationWithBlock:(void (^)(void))block;
//获取队列中的所有任务
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
//获取队列中的任务数量
@property (readonly) NSUInteger operationCount;
/*
队列支持的最大任务并发数
如果为1,则只支持同时处理一个任务,即串行队列,主队列就是串行队列使用主线程执行任务
如果为大于1的数,则支持同时处理多个任务,即并发队列,底层使用线程池管理多个线程来执行任务
默认是-1(NSOperationQueueDefaultMaxConcurrentOperationCount),表示不对最大并发数做限制
*/
@property NSInteger maxConcurrentOperationCount;
//队列是否挂起
@property (getter=isSuspended) BOOL suspended;
//队列的名称
@property (nullable, copy) NSString *name;
/*
取消队列中的所有任务
即所有任务都执行cancel方法,所有任务的cancelled属性都置为YES
*/
- (void)cancelAllOperations;
//阻塞当前线程直到所有任务完成
- (void)waitUntilAllOperationsAreFinished;
//类属性,获取当前队列
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue;
//类属性,获取主队列,任务添加到主队列就会使用主线程执行,主队列的任务并发数为1,即串行队列
@property (class, readonly, strong) NSOperationQueue *mainQueue;
类属性maxConcurrentOperationCount
的值决定了,当前队列是串行还是并发队列。当值是1
时,表示串行队列,大于1
表示并发队列,当值为NSOperationQueueDefaultMaxConcurrentOperationCount
时,表示不对最大任务数做限制。
3、操作数添加依赖
NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
operationQueue.name = @"firstQueue";
[operationQueue addOperation:invocationOperation];
NSLog(@"queue is %@",NSOperationQueue.currentQueue);
NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
NSInteger i = 0;
while (i < 10) {
i++;
NSLog(@"task3:%ld thread:%@",i,NSThread.currentThread);
}
}];
NSBlockOperation *blockOperation4 = [NSBlockOperation blockOperationWithBlock:^{
NSInteger i = 0;
while (i < 10) {
i++;
NSLog(@"task4:%ld thread:%@",i,NSThread.currentThread);
}
}];
NSBlockOperation *blockOperation5 = [NSBlockOperation blockOperationWithBlock:^{
NSInteger i = 0;
while (i < 10) {
i++;
NSLog(@"task5:%ld thread:%@",i,NSThread.currentThread);
}
}];
[blockOperation4 addDependency:blockOperation5];
[blockOperation5 addDependency:blockOperation3];
[operationQueue addOperation:blockOperation4];//设置操作数依赖
[operationQueue addOperation:blockOperation5];
[operationQueue addOperation:blockOperation3];
4、自定义NSOperation
子类
在官方文档中指出经自定义NSOperation
子类有两种形式,并发和非并发,非并发形式只需要继承NSOperation
类后实现main
方法即可,而并发形式就比较复杂了,接下来会分别介绍两种形式。
非并发的NSOperation
自定义子类
@interface TestOperation : NSOperation
@property (nonatomic, copy)id obj;
- (instancetype)initWithObject:(id)obj;
@end
interface TestOperation()
@property (nonatomic, assign, getter=isFinished) BOOL finished;
@property (nonatomic, assign, getter=isExecuting) BOOL executing;
@property (nonatomic, assign, getter=isCancelled) BOOL cancelled;
@end
@implementation TestOperation
@synthesize finished = _finished;
@synthesize executing = _executing;
@synthesize cancelled = _cancelled;
- (instancetype)initWithObject:(id)obj {
self = [super init];
if (self) {
self.obj = obj;
}
return self;
}
- (void)start {
[self main];
}
- (void)main {
self.executing = YES;
NSInteger i = 0;
while (i < 10) {
if (self.isCancelled)
{
//任务被取消正确结束前手动设置状态
self.executing = NO;
self.finished = YES;
return;
}
i++;
NSLog(@"TestTask:%ld thread:%@ queue:%@",i,NSThread.currentThread,NSOperationQueue.currentQueue);
}
self.executing = NO;
self.finished = YES;
}
- (void)setCancelled:(BOOL)cancelled {
[self willChangeValueForKey:@"isCancelled"];
_cancelled = cancelled;
[self didChangeValueForKey:@"isCancelled"];
}
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
- (BOOL)isFinished {
return _finished;
}
- (BOOL)isCancelled {
return _cancelled;
}
- (BOOL)isExecuting {
return _cancelled;
}
@end
#######试用自定义NSOperation类
TestOperation *myOperation = [[TestOperation alloc]initWithObject:@"myOperation"];
myOperation.name = @"private operation";
myOperation.completionBlock = ^{
NSLog(@"my operation completed!");
};
// [myOperation start];
[self.operationQueue addOperation:myOperation];
NSLog(@"isFinished is %d",myOperation.isFinished);
NSLog(@"isExecuting is %d",myOperation.isExecuting);
NSLog(@"isCancelled is %d",myOperation.isCancelled);
NSLog(@"operations is %@",self.operationQueue.operations);
认真的:
NSLog(@"isFinished is %d",myOperation.isFinished); NSLog(@"operations is %@",self.operationQueue.operations);
isFinised一直是0
,并且operations里还是包含了myOperation
,不知道为什么任务执行完了,isFinished没有变化,同时operations里仍然有自定义的类。但是在主队列里,isFinished
会是1
。我测试了2个小时,仍然没找到原因,有知道的小伙伴请告知我
。