多线程之NSOperation详解
NSOperation简介
NSOperation是开启多线程其中的一种方式,是个抽象类,通常是使用子类NSInvocationOperation和NSBlockOperation。
1、NSOperation用法
1.1、继承
NSOperation不能直接使用,但可以继承NSOperation,通过实现main方法,把要执行的操作放在main里。
代码如下:
#import "CustomOperation.h"
@implementation CustomOperation
- (void)main{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"0------%@",[NSThread currentThread]);
}
}
/**
继承NSOperation,重写main方法,main方法里添加要执行的操作
在当前线程运行
*/
- (void)customOperation{
CustomOperation *op = [[CustomOperation alloc] init];
[op start];
}
输出结果:

NSOperation 默认情况下都是在当前线程执行
如果要在其他线程执行,如下代码
/**
在其他线程运行
*/
- (void)runInOtherThread{
[NSThread detachNewThreadSelector:@selector(customOperation) toTarget:self withObject:nil];
}

1.2、NSOperation相互依赖
addDependency:相互依赖
removeDependency:移除依赖
/**
addDependency,相互依赖大于优先级
*/
- (void)operation_addDependency
{
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method2) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method3) object:nil];
[op1 addDependency:op2];//op1依赖op2
[op2 addDependency:op3];//op2依赖op3
// [op2 removeDependency:op3];移除依赖
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
}

1.3、NSOperation常用的属性和方法
取消/暂停/恢复操作
- (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; 当前队列中的操作数。
2、NSInvocationOperation用法
2.1、NSInvocationOperation不添加queue
在当前线程串行执行操作
/**
子类:NSInvocationOperation
知识点:不添加queue,串行执行操作
*/
- (void)InvocationOperation_noneQueue{
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method1) object:nil];
[invocationOperation start];
}
输出结果:

3、NSBlockOperation用法
3.1、NSBlockOperation不添加queue
在当前线程串行执行操作
/**
子类:NSBlockOperation
知识点:不添加queue,串行执行操作
*/
- (void)blockOperation_noneQueue{
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3------%@",[NSThread currentThread]);
}
}];
[blockOperation start];
}
输出结果:

3.2、NSBlockOperation --addExecutionBlock
添加额外的操作。可能会在不同线程并行(操作多的情况下),由系统决定。
/**
子类:NSBlockOperation
知识点:addExecutionBlock,添加额外的操作,可能会在不同线程并行
*/
- (void)blockOperation_addExecutionBlock{
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"blockOperationWithBlock------%@",[NSThread currentThread]);
}
}];
[blockOperation addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"addExecutionBlock---1---%@",[NSThread currentThread]);
}
}];
[blockOperation addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"addExecutionBlock---2---%@",[NSThread currentThread]);
}
}];
[blockOperation start];
}
输入结果:

4、NSOperationQueue用法
4.1、NSOperation开启多线程
NSOperation 添加quque才能开启多线程,不然就是在当前线程运行
代码如下:
- (void)queue_NSInvocationOperation_NSBlockOperation{
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method2) object:nil];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
[self method3];
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
//警告:不要即把操作添加到操作队列中,又调用操作的start方法,这样是不允许的!否则运行时直接报错。
// [op1 start];
// [op2 start];
// [op3 start];
}
- (void)method1{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1------%@",[NSThread currentThread]);
}
}
- (void)method2{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2------%@",[NSThread currentThread]);
}
}
- (void)method3{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3------%@",[NSThread currentThread]);
}
}
输入结果:

4.2、NSOperationQueue-- addOperationWithBlock
addOperationWithBlock,开启新线程,并发执行
/**
addOperationWithBlock,开启新线程,并发执行
*/
- (void)queue_addOperationWithBlock
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 2;
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3---%@",[NSThread currentThread]);
}
}];
}

4.3、NSOperationQueue的一些属性
控制并发数,maxConcurrentOperationCount
当maxConcurrentOperationCount=1是串行,
当maxConcurrentOperationCount>1是并行。
优先级,测试过貌似没啥效果,有问题欢迎大家指正
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
服务质量,iOS8后推出的,测试过貌似没啥效果,有问题欢迎大家指正
typedef NS_ENUM(NSInteger, NSQualityOfService) {
NSQualityOfServiceUserInteractive = 0x21,
NSQualityOfServiceUserInitiated = 0x19,
NSQualityOfServiceUtility = 0x11,
NSQualityOfServiceBackground = 0x09,
NSQualityOfServiceDefault = -1
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
4.4、NSOperationQueue线程间通讯
主线程刷新UI
/**
线程间通讯,主线程刷新ui
*/
- (void)queue_otherqueue_mainqueue{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"主线程刷新UI");
NSLog(@"2---%@",[NSThread currentThread]);
}];
}];
}
4.5、监听NSOperationQueue完成情况
用kvo的方式监听queue的operationCount
/**
kvo监听queue的operationCount,监听queue是否执行完成
*/
- (void)queue_kvo_operationCount{
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method2) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method3) object:nil];
self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 1;
[self.queue addOperation:op1];
[self.queue addOperation:op2];
[self.queue addOperation:op3];
[self.queue addObserver:self forKeyPath:@"operationCount" options:(NSKeyValueObservingOptionNew) context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"operationCount:%zi",self.queue.operationCount);
}

4.6、NSOperationQueue常用的属性和方法
取消/暂停/恢复操作
- (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; 获取主队列。
5、 NSOperation、NSOperationQueue 非线程安全线程安全
NSOperation是非线程安全的
线程安全解决方案:可以给线程加锁,在一个线程执行该操作的时候,不允许其他线程进行操作。iOS 实现线程加锁有很多种方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/ge等等各种方式。这里我们使用 NSLock 对象来解决线程同步问题。NSLock 对象可以通过进入锁时调用 lock 方法,解锁时调用 unlock 方法来保证线程安全。
#pragma mark - 线程不安全
/**
线程不安全
*/
- (void)ticketSale_unsafe{
self.totalTicketCount = 50;
NSBlockOperation *salewin1 = [NSBlockOperation blockOperationWithBlock:^{
[self ticketBeginSale];
}];
NSBlockOperation *salewin2 = [NSBlockOperation blockOperationWithBlock:^{
[self ticketBeginSale];
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:salewin1];
[queue addOperation:salewin2];
}
- (void)ticketBeginSale{
while (1) {
[self.lock lock];//加锁
if (self.totalTicketCount > 0) {
self.totalTicketCount--;
NSLog(@"剩余票数:%zi",self.totalTicketCount);
[NSThread sleepForTimeInterval:0.2];
}
[self.lock unlock];//解锁
if (self.totalTicketCount <= 0) {
NSLog(@"票已售完~~~~~~~~");
break;
}
}
}
