iOS NSOperationQueue 和 NSOperation 区别

//联系人:石虎QQ: 1224614774昵称:嗡嘛呢叭咪哄

1. 为什么优先使用NSOperationQueue而不是GCD

曾经我有一段时间我非常喜欢使用GCD来进行并发编程,因为虽然它是C的api,但是使用起来却非常简单和方便, 不过这样也就容易使开发者忘记并发编程中的许多注意事项和陷阱。

比如你可能写过类似这样的代码(这样来请求网络数据):

dispatch_async(_Queue, ^{//请求数据NSData*data = [NSDatadataWithContentURL:[NSURLURLWithString:@"http://domain.com/a.png"]];dispatch_async(dispatch_get_main_queue(), ^{        [selfrefreshViews:data];    });});

没错,它是可以正常的工作,但是有个致命的问题:这个任务是无法取消的。

dataWithContentURL:是同步的拉取数据,它会一直阻塞线程直到完成请求,如果是遇到了超时的情况,它在这个时间内会一直占有这个线程;在这个期间并发队列就需要为其他任务新建线程,这样可能导致性能下降等问题。

因此我们不推荐这种写法来从网络拉取数据。

操作队列(operation queue)是由 GCD 提供的一个队列模型的 Cocoa 抽象。GCD 提供了更加底层的控制,而操作队列则在 GCD 之上实现了一些方便的功能,这些功能对于 app 的开发者来说通常是最好最安全的选择。NSOperationQueue相对于GCD来说有以下优点:

提供了在 GCD 中不那么容易复制的有用特性。

可以很方便的取消一个NSOperation的执行

可以更容易的添加任务的依赖关系

提供了任务的状态:isExecuteing, isFinished.

名词: 本文中提到的 “任务”, “操作” 即代表要再NSOperation中执行的事情。

2. Operation Queues的使用

2.1 NSOperationQueue

NSOperationQueue有两种不同类型的队列:主队列和自定义队列。主队列运行在主线程之上,而自定义队列在后台执行。在两种类型中,这些队列所处理的任务都使用NSOperation的子类来表述。

NSOperationQueue*mainQueue = [NSOperationQueuemainQueue];//主队列NSOperationQueue*queue = [[NSOperationQueuealloc] init];//自定义队列NSBlockOperation*operation = [NSBlockOperationblockOperationWithBlock:^{//任务执行}];    [queue addOperation:operation];

我们可以通过设置maxConcurrentOperationCount属性来控制并发任务的数量,当设置为 1时, 那么它就是一个串行队列。主对列默认是串行队列,这一点和dispatch_queue_t是相似的。

2.2 NSOperation

你可以使用系统提供的一些现成的NSOperation的子类, 如NSBlockOperation、NSInvocationOperation等(如上例子)。你也可以实现自己的子类, 通过重写main或者start方法 来定义自己的operations。

使用main方法非常简单,开发者不需要管理一些状态属性(例如 isExecuting 和 isFinished),当 main 方法返回的时候,这个 operation 就结束了。这种方式使用起来非常简单,但是灵活性相对重写 start 来说要少一些, 因为main方法执行完就认为operation结束了,所以一般可以用来执行同步任务。

@implementationYourOperation- (void)main{// 任务代码...}@end

如果你希望拥有更多的控制权,或者想在一个操作中可以执行异步任务,那么就重写start方法, 但是注意:这种情况下,你必须手动管理操作的状态, 只有当发送 isFinished的 KVO 消息时,才认为是 operation 结束。

@implementationYourOperation- (void)start{self.isExecuting =YES;// 任务代码 ...}- (void)finish//异步回调{self.isExecuting =NO;self.isFinished =YES;}@end

当实现了start方法时,默认会执行start方法,而不执行main方法。为了让操作队列能够捕获到操作的改变,需要将状态的属性以配合 KVO的方式进行实现。如果你不使用它们默认的 setter 来进行设置的话你就需要在合适的时候发送合适的 KVO消息。

需要手动管理的状态有:

isExecuting

代表任务正在执行中

isFinished

代表任务已经执行完成

isCancelled

代表任务已经取消执行

手动的发送 KVO消息, 通知状态更改如下 :

[self willChangeValueForKey:@"isCancelled"];_isCancelled=YES;[self didChangeValueForKey:@"isCancelled"];

为了能使用操作队列所提供的取消功能,你需要在长时间操作中时不时地检查 isCancelled属性, 比如在一个长的循环中:

@implementationMyOperation- (void)main{while(notDone && !self.isCancelled) {// 任务处理}}@end

按照以上说法,很多情况下我们都需要自定义operation来实现需求。

在这里,我参考了AFNetworking的底层实现

#import "AFURLConnectionOperation.h"

有兴趣的朋友可以深入研究一下

以下是我写的源码:(新鲜出炉)希望对朋友们有所帮助,喜欢就直接点儿

QWSSendOrderOperation.h文件

////  QWSSendOrderOperation.h//  WEBluetooth////  Created by Tilink on 15/7/24.//  Copyright (c) 2015年 Tilink. All rights reserved.//#importtypedefenum:NSUInteger{    orderStyleAuthentication =1,// 鉴权orderStyleWritePassword,// 写密码orderStyleLockOrUnLock,// 加解锁orderStyleOpenDevBarrel,// 开坐桶orderStyleLockEMachinery,// 锁定电机orderStyleSetLockEMCode,// 设置索电机码orderStyleFindDev,// 寻车orderStyleClearDis// 清除里程} OrderStytle;// 发送指令后的回调    0:OK    1:未知错误  2:鉴权失败typedefvoid(^ExecutingBlock)();typedefvoid(^CompleteBlock)(NSIntegererrCode,NSString* errDesc,iddata);@interfaceQWSSendOrderOperation:NSOperation// 发送数据@property(assign,nonatomic) OrderStytle orderStyle;@property(copy,nonatomic)NSString* orderContent;// 执行块@property(copy,nonatomic) ExecutingBlock executingBlock;// 请求完成后的回调@property(copy,nonatomic) CompleteBlock finishedBlock;/**

*  @author ThinkerDown, 15-07-24 14:07:59

*

*  @brief  <#Description#>

*

*  @param orderStyle    <#orderStyle description#>

*  @param orderContent  <#orderContent description#>

*  @param executingBlock <#executingBlock description#>

*  @param finishedBlock  <#finishedBlock description#>

*

*  @return <#return value description#>

*/-(instancetype)initWithOrderStyle:(OrderStytle)orderStyle                    orderContent:(NSString*)orderContent                  executingBlock:(ExecutingBlock)executingBlock                        finished:(CompleteBlock)finishedBlock;///-------------------------------/// @name Accessing Run Loop Modes///-------------------------------/**

The run loop modes in which the operation will run on the network thread. By default, this is a single-member set containing `NSRunLoopCommonModes`.

*/@property(nonatomic,strong)NSSet*runLoopModes;// 完成- (void)finish;@end

QWSSendOrderOperation.m文件

////  QWSSendOrderOperation.m//  WEBluetooth////  Created by Tilink on 15/7/24.//  Copyright (c) 2015年 Tilink. All rights reserved.//#import"QWSSendOrderOperation.h"typedefNS_ENUM(NSInteger, QWSOperationState) {    QWSOperationPausedState    =-1,    QWSOperationReadyState      =1,    QWSOperationExecutingState  =2,    QWSOperationFinishedState  =3,};staticNSString*constkQWSSendOrderLockName =@"com.qws.sendorder.operation.lock";NSString*constQWSSendOrderOperationDidStartNotification  =@"com.qws.sendorder.operation.start";NSString*constQWSSendOrderOperationDidFinishNotification =@"com.qws.sendorder.operation.finish";staticinlineNSString* QWSKeyPathFromOperationState(QWSOperationState state) {switch(state) {caseQWSOperationReadyState:return@"isReady";caseQWSOperationExecutingState:return@"isExecuting";caseQWSOperationFinishedState:return@"isFinished";caseQWSOperationPausedState:return@"isPaused";default: {#pragma clang diagnostic push#pragma clang diagnostic ignored"-Wunreachable-code"return@"state";#pragma clang diagnostic pop}    }}staticinlineBOOLQWSStateTransitionIsValid(QWSOperationState fromState, QWSOperationState toState,BOOLisCancelled) {switch(fromState) {caseQWSOperationReadyState:switch(toState) {caseQWSOperationPausedState:caseQWSOperationExecutingState:returnYES;caseQWSOperationFinishedState:returnisCancelled;default:returnNO;            }caseQWSOperationExecutingState:switch(toState) {caseQWSOperationPausedState:caseQWSOperationFinishedState:returnYES;default:returnNO;            }caseQWSOperationFinishedState:returnNO;caseQWSOperationPausedState:returntoState == QWSOperationReadyState;default: {#pragma clang diagnostic push#pragma clang diagnostic ignored"-Wunreachable-code"switch(toState) {caseQWSOperationPausedState:caseQWSOperationReadyState:caseQWSOperationExecutingState:caseQWSOperationFinishedState:returnYES;default:returnNO;            }        }#pragma clang diagnostic pop}}@interfaceQWSSendOrderOperation()@property(readwrite,nonatomic,assign) QWSOperationState  state;@property(readwrite,nonatomic,strong)NSRecursiveLock*lock;- (void)operationDidStart;- (void)finish;- (void)cancelConnection;@end@implementationQWSSendOrderOperation+ (void)networkRequestThreadEntryPoint:(id)__unused object {@autoreleasepool{        [[NSThreadcurrentThread] setName:@"QWSSendOrder"];NSRunLoop*runLoop = [NSRunLoopcurrentRunLoop];        [runLoop addPort:[NSMachPortport] forMode:NSDefaultRunLoopMode];        [runLoop run];    }}+ (NSThread*)networkRequestThread {staticNSThread*_networkRequestThread =nil;staticdispatch_once_toncePredicate;dispatch_once(&oncePredicate, ^{        _networkRequestThread = [[NSThreadalloc] initWithTarget:selfselector:@selector(networkRequestThreadEntryPoint:) object:nil];        [_networkRequestThread start];    });return_networkRequestThread;}/**

*  @author ThinkerDown, 15-07-24 14:07:59

*

*  @brief  <#Description#>

*

*  @param orderStyle    <#orderStyle description#>

*  @param orderContent  <#orderContent description#>

*  @param executingBlock <#executingBlock description#>

*  @param finishedBlock  <#finishedBlock description#>

*

*  @return <#return value description#>

*/-(instancetype)initWithOrderStyle:(OrderStytle)orderStyle                    orderContent:(NSString*)orderContent                  executingBlock:(ExecutingBlock)executingBlock                        finished:(CompleteBlock)finishedBlock {if(self= [superinit]) {        _state = QWSOperationReadyState;self.lock = [[NSRecursiveLockalloc] init];self.lock.name = kQWSSendOrderLockName;self.orderStyle = orderStyle;self.orderContent = orderContent;self.runLoopModes = [NSSetsetWithObject:NSRunLoopCommonModes];self.executingBlock = executingBlock;self.finishedBlock = finishedBlock;    }returnself;}#pragma mark -- (void)setState:(QWSOperationState)state {if(!QWSStateTransitionIsValid(self.state, state, [selfisCancelled])) {return;    }    [self.lock lock];NSString*oldStateKey = QWSKeyPathFromOperationState(self.state);NSString*newStateKey = QWSKeyPathFromOperationState(state);    [selfwillChangeValueForKey:newStateKey];    [selfwillChangeValueForKey:oldStateKey];    _state = state;    [selfdidChangeValueForKey:oldStateKey];    [selfdidChangeValueForKey:newStateKey];    [self.lock unlock];}- (void)pause {if([selfisPaused] || [selfisFinished] || [selfisCancelled]) {return;    }    [self.lock lock];if([selfisExecuting]) {        [selfperformSelector:@selector(operationDidPause) onThread:[[selfclass] networkRequestThread] withObject:nilwaitUntilDone:NOmodes:[self.runLoopModes allObjects]];dispatch_async(dispatch_get_main_queue(), ^{NSNotificationCenter*notificationCenter = [NSNotificationCenterdefaultCenter];            [notificationCenter postNotificationName:QWSSendOrderOperationDidFinishNotification object:self];        });    }self.state = QWSOperationPausedState;    [self.lock unlock];}- (void)operationDidPause {    [self.lock lock];// 发送指令不能取消[self.lock unlock];}- (BOOL)isPaused {returnself.state == QWSOperationPausedState;}- (void)resume {if(![selfisPaused]) {return;    }    [self.lock lock];self.state = QWSOperationReadyState;    [selfstart];    [self.lock unlock];}#pragma mark - NSOperation- (BOOL)isReady {returnself.state == QWSOperationReadyState && [superisReady];}- (BOOL)isExecuting {returnself.state == QWSOperationExecutingState;}- (BOOL)isFinished {returnself.state == QWSOperationFinishedState;}- (BOOL)isConcurrent {returnYES;}- (void)start {    [self.lock lock];if([selfisCancelled]) {        [selfperformSelector:@selector(cancelConnection) onThread:[[selfclass] networkRequestThread] withObject:nilwaitUntilDone:NOmodes:[self.runLoopModes allObjects]];    }elseif([selfisReady]) {self.state = QWSOperationExecutingState;        [selfperformSelector:@selector(operationDidStart) onThread:[[selfclass] networkRequestThread] withObject:nilwaitUntilDone:NOmodes:[self.runLoopModes allObjects]];    }    [self.lock unlock];}- (void)operationDidStart {    [self.lock lock];if(![selfisCancelled]) {if(_executingBlock) {self.executingBlock();        }    }    [self.lock unlock];dispatch_async(dispatch_get_main_queue(), ^{        [[NSNotificationCenterdefaultCenter] postNotificationName:QWSSendOrderOperationDidStartNotification object:self];    });}- (void)finish {    [self.lock lock];self.state = QWSOperationFinishedState;    [self.lock unlock];dispatch_async(dispatch_get_main_queue(), ^{        [[NSNotificationCenterdefaultCenter] postNotificationName:QWSSendOrderOperationDidFinishNotification object:self];    });}- (void)cancel {    [self.lock lock];if(![selfisFinished] && ![selfisCancelled]) {        [supercancel];if([selfisExecuting]) {            [selfperformSelector:@selector(cancelConnection) onThread:[[selfclass] networkRequestThread] withObject:nilwaitUntilDone:NOmodes:[self.runLoopModes allObjects]];        }    }    [self.lock unlock];}- (void)cancelConnection {if(![selfisFinished]) {        [selffinish];    }}@end

谢谢!!!

你可能感兴趣的:(iOS NSOperationQueue 和 NSOperation 区别)