一、NSOperationQueue简介
1.概述:
NSOperationQueue类管理NSOperation对象的执行。NSOperationQueue可以被称为操作队列,NSOperation可以被称为操作。操作被添加到操作队列之后,操作队列会根据操作对象的优先级或者相互之间的依赖关系来执行操作。一个应用程序可以创建多个操作队列,每个操作队列分别管理若干操作。
2.什么时候操作会被移除:
当操作被添加到队列中以后,我们不能从操作队列中直接移除操作对象。当一个操作对象的任务完成之后才会被自动移除。任务完成的含义:操作的start方法被调用,并且isFinished方法返回YES。
3.如何取消任务执行:
调用操作对象cancel方法并不能使操作马上停止执行。当NSOperation的cancel方法被调用后,如果操作不在队列中,这个方法会将操作的isFinished设为YES,如果在操作队列中,这个方法会将操作对象的isCancelled状态设为YES,并且isReady设为YES,让队列调用它的start方法。在start或者main方法实现中,我们应该检查isCancelled和isFinished属性,如果任意一个为YES,就不执行操作,直接返回,如果是并发操作,让isFinished方法返回YES,如果是非并发操作,设置isFinished值为YES。
4.操作队列的相关API:
(1)创建队列:
可以调用[[NSOperationQueue alloc] init]方法新建一个操作队列,或者调用类方法currentQueue、mainQueue方法获取现存的操作队列。currentQueue一般在NSOperation对象子类中调用,用来返回启动当前操作的队列,如果NSOperation在非运行状态或者在NSOperation外部调用此方法,一般会返回nil。mainQueue方法返回和主线程绑定的操作队列。
(2)增加操作:
addOperation: 增加一个操作对象。
addOperations:waitUntilFinished: 增加一个操作对象数组,可以阻塞当前线程等待所有操作完成。
addOperationWithBlock: 以block的方式添加操作,blcok必须为无参数无返回数据。
(3)最大并发数:
maxConcurrentOperationCount :最大并发数属性,默认为-1,表示并发数无限制,由系统根据环境条件动态决定。
setMaxConcurrentOperationCount:设置最大并发数。
(4)其它:
operations :获取添加到队列中的所有操作对象。
operationCount :获取添加到队列中的操作对象的数量。
setSuspended: 设置队列是否暂停安排操作执行。
isSuspended :获取队列是否处在暂停安排操作执行的状态。
cancelAllOperations:调用所有操作对象的cancel方法。
waitUntilAllOperationsAreFinished:阻塞当前线程,等待所有操作都完成。
5.KVO兼容的属性
operations - 只读
operationCount - 只读
maxConcurrentOperationCount - 可读写
suspended - 可读写
name -可读写
6.内部实现:
在iOS4.0及以后,NSOperationQueue内部实现使用GCD,iOS4.0之前,使用NSThread实现。
二、NSOperation简介
1.概述:
NSOperation类代表单个任务对象,是一个抽象类,因此他不能直接使用它,可以实现它的子类,也可以使用类库中已存在的子类,比如NSInvocationOperation和NSBlockOperation,可以比较简单的创建操作对象。操作对象只能被执行一次,只要被执行过,就不能再重新执行第二次。
2.依赖关系:
(1)调用NSOperation的addDependency或者removeDependency可以给一个操作对象添加一个它所依赖的操作对象、删除一个所依赖的操作对象。调用dependencies方法可以获取一个操作所依赖的所有操作对象。
(2)只有当一个操作对象的所有依赖对象都执行完毕后,它的isReady属性才可能会成为YES,从而被队列执行。
(3)NSOperation本身并不区分它的依赖对象是执行失败还是成功,只判断操作是否执行完毕(即有可能是通过cancel方法完成的)。
(4)如果一个操作对象被调用了cancel方法,但是还有它依赖的操作对象没有完成,它所依赖的操作对象将被忽略,当前操作对象不再等待它们完成。
3.执行优先级
(1)调用queuePriority、setQueuePriority:方法可以获取和设置操作对象的执行优先级。
(2)类库中定义了几个优先级变量:
NSOperationQueuePriorityVeryLow
NSOperationQueuePriorityLow
NSOperationQueuePriorityNormal
NSOperationQueuePriorityHigh
NSOperationQueuePriorityVeryHigh
最高对应的数字为8,最低-8,如果手动设置数字,setQueuePriority方法会自动切换成对应的最合适的数字。
(3)优先级只表示此操作对象的优先级和其它对象优先级的大小关系,不能精确定义两个对象的执行顺序,不能使用优先级这个方式来实现依赖关系。
4.KVO兼容的属性
isCancelled - 只读,操作是不是被取消,cancel方法会改变此变量。
isConcurrent - 只读,是不是并发
isExecuting - 只读,是不是正在执行
isFinished - 只读,是不是已经完成
isReady - 只读,是不是已经准备好执行
dependencies - 只读,操作对象的依赖关系
queuePriority - 可读写,操作对象在队列中的优先级
completionBlock - 可读写,操作完成之后执行的Block
5.并发操作和非并发操作
(1)如果打算手动执行操作对象,我们可以把操作对象设计成非并发的,也可以涉及成并发的。操作对象默认是非并发的。
(2)在非并发的操作对象中,操作任务是以同步的方式实现的,当调用start方法时,任务在当前线程执行,当start方法返回时,任务已经完成,执行期间线程被阻塞。
(3)并发操作对象的任务以异步的方式执行,当调用start方法时,start方法立即返回,不阻塞当前线程。因为在start方法中,任务代码使用新线程或者异步API实现。
(4)如果打算总是以队列的方式执行操作,最简单的方式是将操作对象设计成非并发的。定义并发操作对象需要更过的工作,因为实现并发操作对象需要实现更多的方法,而且我们必须去监听任务的状态以及使用KVO手动通知的方式报告状态。当非并发操作在队列中执行时,队列会自动建立新线程来执行操作,最后结果也是异步的。所以如果总是以队列的方式执行操作对象,是没有理由使用并发操作的。
6.子类化NSOperation
(1)如果是非并发操作,只需要实现main方法即可。
(2)如果是并发操作,最少要实现一下四个方法:start、isConcurrent、isExecuting、isFinished。
(3)在main方法和start方法实现中,在开始执行任务代码前,首先应该检查对象状态,如果
isCancelled等于YES或者isFinished等于YES,则不用执行任务代码。如果是并发操作,让isFinished方法返回YES,如果是非并发操作,设置isFinished值为YES。
(4)在重写isConcurrent、isExecuting、isFinished方法的同时,还要对值发生改变的key使用KVO手动的方式触发通知,否则操作不会被认为执行结束。代码逻辑:在以上三个方法中通过判断本地的状态标记变量来判断状态值,并返回;在更改我们的本地标记变量值时,通过KVO发送通知,然后OperationQueue就会去调用以上三个方法来判断Operation执行状态,如果isFinished方法返回YES,则操作完成,任务结束。
7.异步NSOperation实现示例:
typedef enum
{
RequestStateReady = 0,
RequestStateExecuting,
RequestStateFinished
}RequestState;
@interface RequestOperation ()
@property(nonatomic,strong) NSURLConnection *connection;
@property(nonatomic,strong) NSMutableData *resultData;
@property(nonatomic,assign) RequestState requestState;
@end
@implementation RequestOperation
-(id)init{
self = [super init];
if (self) {
[self willChangeValueForKey:@"isReady"];
self.requestState = RequestStateReady;
[self didChangeValueForKey:@"isReady"];
}
return self;
}
#pragma mark - Operation实现父类方法
-(void)start{
[self exeRequest];
}
-(BOOL)isConcurrent{
return YES;
}
-(BOOL)isReady{
return self.requestState == RequestStateReady && [super isReady];
}
-(BOOL)isExecuting{
return self.requestState == RequestStateExecuting;
}
-(BOOL)isFinished{
return self.requestState == RequestStateFinished;
}
#pragma mark - 发起请求
-(void)exeRequest{
if (self.isCancelled == YES || self.isFinished == YES) {
return;
}
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.baidu.com/"]];
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[self.connection start];
[[NSRunLoop currentRunLoop] run];
[self willChangeValueForKey:@"isExecuting"];
self.requestState = RequestStateExecuting;
[self didChangeValueForKey:@"isExecuting"];
}
#pragma mark - 请求代理实现
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
self.resultData = [[NSMutableData alloc] initWithCapacity:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.resultData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[self willChangeValueForKey:@"isFinished"];
self.requestState = RequestStateFinished;
[self didChangeValueForKey:@"isFinished"];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
[self willChangeValueForKey:@"isFinished"];
self.requestState = RequestStateFinished;
[self didChangeValueForKey:@"isFinished"];
}
@end