一 NSOperation简介
@property(nonatomic,strong) NSInvocationOperation *simpleOperation;
@property(nonatomic,strong) NSBlockOperation *blockOpertaion;
@synthesize simpleOperation = _simpleOperation;
@synthesize blockOpertaion = _blockOpertaion;
- (void) simpleOperationEntry:(id) paramObject {
NSLog(@"Parameter Object = %@",paramObject);
NSLog(@"Main Thread = %@",[NSThread mainThread]);
NSLog(@"Current Thread = %@",[NSThread currentThread]);
}
调用代码
-(void) viewDidAppear:(BOOL)animated {
self.blockOpertaion = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Main Thread = %@",[NSThread mainThread]);
NSLog(@"Current Thread = %@",[NSThread currentThread]);
NSUInteger count = 0;
for (count=0; count<10; ++count) {
NSLog(@"count= %d",count);
}
}];
}
2013-10-24 16:28:46.267 OperationDemo[5459:c07] Parameter Object = 123
2013-10-24 16:28:46.269 OperationDemo[5459:c07] Main Thread = <NSThread: 0x717ae10>{name = (null), num = 1}
2013-10-24 16:38:07.240 OperationDemo[5478:c07] count= 0
2013-10-24 16:38:07.241 OperationDemo[5478:c07] count= 1
2013-10-24 16:38:07.241 OperationDemo[5478:c07] count= 2
2013-10-24 16:38:07.241 OperationDemo[5478:c07] count= 3
2013-10-24 16:38:07.241 OperationDemo[5478:c07] count= 4
2013-10-24 16:38:07.242 OperationDemo[5478:c07] count= 5
2013-10-24 16:38:07.242 OperationDemo[5478:c07] count= 6
2013-10-24 16:38:07.242 OperationDemo[5478:c07] count= 7
2013-10-24 16:38:07.243 OperationDemo[5478:c07] count= 8
2013-10-24 16:38:07.243 OperationDemo[5478:c07] count= 9
@interface WBViewController ()
@property (nonatomic, strong) NSOperationQueue *operationQueue;
@property (nonatomic, strong) NSInvocationOperation *firstOperation;
@property (nonatomic, strong) NSInvocationOperation *secondOperation;
@implementation WBViewController
@synthesize operationQueue = _operationQueue;
@synthesize firstOperation =_firstOperation;
@synthesize secondOperation =_secondOperation;
- (void) firstOperationEntry:(id)paramObject{
NSLog(@"%s", __FUNCTION__);
NSLog(@"Parameter Object = %@", paramObject);
NSLog(@"Main Thread = %@", [NSThread mainThread]);
NSLog(@"Current Thread = %@", [NSThread currentThread]);
}
- (void) secondOperationEntry:(id)paramObject{
NSLog(@"%s", __FUNCTION__);
NSLog(@"Parameter Object = %@", paramObject);
NSLog(@"Main Thread = %@", [NSThread mainThread]);
NSLog(@"Current Thread = %@", [NSThread currentThread]);
self.firstOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(firstOperationEntry:) object:[NSNumber numberWithInteger:111]];
self.secondOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(secondOperationEntry:) object:[NSNumber numberWithInteger:222] ];
2013-10-25 16:41:57.103 OperationDemo[8424:1903] Parameter Object = 111
2013-10-25 16:41:57.103 OperationDemo[8424:1903] Main Thread = <NSThread: 0x71216f0>{name = (null), num = 1}
2013-10-25 16:41:57.105 OperationDemo[8424:1903] Current Thread = <NSThread: 0x7579120>{name = (null), num = 3}
2013-10-25 16:41:57.102 OperationDemo[8424:4607] -[WBViewController secondOperationEntry:]
2013-10-25 16:41:57.106 OperationDemo[8424:4607] Parameter Object = 222
2013-10-25 16:41:57.106 OperationDemo[8424:4607] Main Thread = <NSThread: 0x71216f0>{name = (null), num = 1}
2013-10-25 16:41:57.107 OperationDemo[8424:4607] Current Thread = <NSThread: 0x7578fd0>{name = (null), num = 4}
NSOperation是IOS是实现程序并行的一种方法,它是在IOS实现多线程编程的重要的方式之一。其实它就是对线程的一种高度的抽象封装,如果我们熟悉Windows 编程的话这个NSOperation中定义的main方法就相当于线程函数,
如果你也熟悉Java,NSOperation就和java.lang.Runnable接口很相似。但是NSOperation做了更加高度的抽象,使用起来也更加的方便。
二 NSOperation的使用
首先介绍两种特殊的NSOperation :
NSInvocationOperation和NSBlockOperation
NSInvocationOperation
用于调用一个对象内部的方法
NSBlockOperation 用于封装一个代码块,执行一个代码块中的代码
其实这两个类的本质都是用来封装要进行异步执行的任务的,可以直接用一个方法也可以使用代码块
下面我们来看一个使用它们的例子:
.h文件
@interface WBViewController ()@property(nonatomic,strong) NSInvocationOperation *simpleOperation;
@property(nonatomic,strong) NSBlockOperation *blockOpertaion;
@end
.m文件
@implementation WBViewController@synthesize simpleOperation = _simpleOperation;
@synthesize blockOpertaion = _blockOpertaion;
- (void) simpleOperationEntry:(id) paramObject {
NSLog(@"Parameter Object = %@",paramObject);
NSLog(@"Main Thread = %@",[NSThread mainThread]);
NSLog(@"Current Thread = %@",[NSThread currentThread]);
}
调用代码
-(void) viewDidAppear:(BOOL)animated {
NSNumber *simpleObject = [NSNumber numberWithInt:123];
self.simpleOperation = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(simpleOperationEntry:)
self.simpleOperation = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(simpleOperationEntry:)
object:simpleObject];
[self.simpleOperation start];
[self.simpleOperation start];
self.blockOpertaion = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Main Thread = %@",[NSThread mainThread]);
NSLog(@"Current Thread = %@",[NSThread currentThread]);
NSUInteger count = 0;
for (count=0; count<10; ++count) {
NSLog(@"count= %d",count);
}
}];
[self.blockOpertaion start];
}
运行结果:
2013-10-24 16:28:46.269 OperationDemo[5459:c07] Main Thread = <NSThread: 0x717ae10>{name = (null), num = 1}
2013-10-24 16:28:46.269 OperationDemo[5459:c07] Current Thread = <NSThread: 0x717ae10>{name = (null), num = 1}
2013-10-24 16:38:07.240 OperationDemo[5478:c07] Main Thread = <NSThread: 0x7184650>{name = (null), num = 1}
2013-10-24 16:38:07.240 OperationDemo[5478:c07] Current Thread = <NSThread: 0x7184650>{name = (null), num = 1}2013-10-24 16:38:07.240 OperationDemo[5478:c07] count= 0
2013-10-24 16:38:07.241 OperationDemo[5478:c07] count= 1
2013-10-24 16:38:07.241 OperationDemo[5478:c07] count= 2
2013-10-24 16:38:07.241 OperationDemo[5478:c07] count= 3
2013-10-24 16:38:07.241 OperationDemo[5478:c07] count= 4
2013-10-24 16:38:07.242 OperationDemo[5478:c07] count= 5
2013-10-24 16:38:07.242 OperationDemo[5478:c07] count= 6
2013-10-24 16:38:07.242 OperationDemo[5478:c07] count= 7
2013-10-24 16:38:07.243 OperationDemo[5478:c07] count= 8
2013-10-24 16:38:07.243 OperationDemo[5478:c07] count= 9
注意:
1.通过输出看以看出,这两个Operation都在主线程中执行的,NSInvocationOperation和NSBlockOperation的执行默认会在调用他们的函数所在的线程中去执行,例如我们在-(void) viewDidAppear:(BOOL)animated 函数中创建并启动一个operation的话,在没有使用NSOperationQueue的情况下,它会在
-(void) viewDidAppear:(BOOL)animated所在的线程内即主线程中去执行。
三 使用 NSOperationQueue
NSOperationQueue是用来管理Operation的,可
以把NSOperationQueue看作一个线程池,可往线程池中添加操作(NSOperation)到队列中。
看下面这段代码:
@property (nonatomic, strong) NSOperationQueue *operationQueue;
@property (nonatomic, strong) NSInvocationOperation *firstOperation;
@property (nonatomic, strong) NSInvocationOperation *secondOperation;
@end
@synthesize operationQueue = _operationQueue;
@synthesize firstOperation =_firstOperation;
@synthesize secondOperation =_secondOperation;
NSLog(@"%s", __FUNCTION__);
NSLog(@"Parameter Object = %@", paramObject);
NSLog(@"Main Thread = %@", [NSThread mainThread]);
NSLog(@"Current Thread = %@", [NSThread currentThread]);
}
- (void) secondOperationEntry:(id)paramObject{
NSLog(@"%s", __FUNCTION__);
NSLog(@"Parameter Object = %@", paramObject);
NSLog(@"Main Thread = %@", [NSThread mainThread]);
NSLog(@"Current Thread = %@", [NSThread currentThread]);
}
调用代码:
self.secondOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(secondOperationEntry:) object:[NSNumber numberWithInteger:222] ];
[self.firstOperation addDependency:self.secondOperation];
输出结果:
2013-10-25 16:41:57.101 OperationDemo[8424:1903] -[WBViewController firstOperationEntry:]2013-10-25 16:41:57.103 OperationDemo[8424:1903] Parameter Object = 111
2013-10-25 16:41:57.103 OperationDemo[8424:1903] Main Thread = <NSThread: 0x71216f0>{name = (null), num = 1}
2013-10-25 16:41:57.105 OperationDemo[8424:1903] Current Thread = <NSThread: 0x7579120>{name = (null), num = 3}
2013-10-25 16:41:57.102 OperationDemo[8424:4607] -[WBViewController secondOperationEntry:]
2013-10-25 16:41:57.106 OperationDemo[8424:4607] Parameter Object = 222
2013-10-25 16:41:57.106 OperationDemo[8424:4607] Main Thread = <NSThread: 0x71216f0>{name = (null), num = 1}
2013-10-25 16:41:57.107 OperationDemo[8424:4607] Current Thread = <NSThread: 0x7578fd0>{name = (null), num = 4}
注意:1.通过观测我们发现两个操作分别线程号为num3和num=4的线程中运行,从而实现了并行执行
b.重写 isStart方法 操作队列会调用该方法来执行操作
c.重写 isCancelled方法 操作队列会周期性调用该方法查看当前操作的执行情况
d.重写 main方法,并创建自己的自动回收池,在main函数返回前来释放资源回收池
e.重写 isFinished和isExecuting方法来反馈跟踪操作执行情况
@interface CountingOperation : NSOperation
@property(nonatomic,assign) BOOL isFinished;
@property(nonatomic,assign) BOOL isExecuting;
-(id) initWithStartingCount:(NSInteger) startCount
endingCount:(NSInteger) end;
@end
@interface CountingOperation()
@property(nonatomic,assign) NSInteger startCount;
@property(nonatomic,assign) NSInteger end;
@end
@implementation CountingOperation
@synthesize startCount = _startCount;
@synthesize end = _end;
@synthesize isFinished = _isFinished;
@synthesize isExecuting = _isExecuting;
-(id) init
{
return [self initWithStartingCount:0 endingCount:100];
}
-(id) initWithStartingCount:(NSInteger) startCount
endingCount:(NSInteger) end
{
if (self = [super init])
{
self.startCount = startCount;
self.end = end;
}
return self;
}
//Conflicting return type in implementation of 'start': 'NSInteger' (aka 'int') vs 'void'
- (void) start
{
if ([self isFinished])
{
[self willChangeValueForKey:@"isFinished"];
self.isFinished = YES;
[self didChangeValueForKey:@"isFinished"];
return;
}
else
{
/* If this operation is *not* cancelled */
/* KVO compliance */
[self willChangeValueForKey:@"isExecuting"];
self.isExecuting = YES;
/* Call the main method from inside the start method */
[self didChangeValueForKey:@"isExecuting"];
[self main];
}
}
- (void) main
{
@try {
/* Here is the autorelease pool */
@autoreleasepool
{
NSLog(@"######:Current Thread = %@", [NSThread currentThread]);
/* Keep a local variable here that must get set to YES whenever we are done with the task */
BOOL taskIsFinished = NO;
/* Create a while loop here that only exists
if the taskIsFinished variable is set to YES or the operation has been cancelled */
while (taskIsFinished == NO && [self isCancelled] == NO)
{
/* Perform the task here */
NSLog(@"Main Thread = %@", [NSThread mainThread]);
NSLog(@"Current Thread = %@", [NSThread currentThread]);
NSUInteger counter = self.startCount;
for (counter = self.startCount;counter < self.end;counter++)
{
NSLog(@"Count = %lu", (unsigned long)counter);
}
/* Very important. This way we can get out of the
loop and we are still complying with the cancellation rules of operations */
taskIsFinished = YES;
}
/* KVO compliance. Generate the required KVO notifications */
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
self.isFinished = YES;
self.isExecuting = NO;
[self didChangeValueForKey:@"isFinished"];
[self didChangeValueForKey:@"isExecuting"];
}
}
@catch (NSException * e)
{
NSLog(@"Exception %@", e);
}
}
- (BOOL) isFinished{
/* Simply return the value */
return(_isFinished);
}
- (BOOL) isExecuting{
/* Simply return the value */
return(_isExecuting);
}
- (BOOL) isConcurrent{
return YES;
}
2013 - 10 - 25 17 : 06 : 05.607 OperationDemo[ 8562 : 4803 ] Current Thread = <NSThread: 0x717b350 >{name = (null), num = 3 }
2013 - 10 - 25 17 : 06 : 05.608 OperationDemo[ 8562 : 4803 ] Count = 0
2013 - 10 - 25 17 : 06 : 05.616 OperationDemo[ 8562 : 4803 ] Count = 1
2013 - 10 - 25 17 : 06 : 05.616 OperationDemo[ 8562 : 4803 ] Count = 2
2013 - 10 - 25 17 : 06 : 05.617 OperationDemo[ 8562 : 4803 ] Count = 3
2013 - 10 - 25 17 : 06 : 05.617 OperationDemo[ 8562 : 4803 ] Count = 4
2013 - 10 - 25 17 : 06 : 05.617 OperationDemo[ 8562 : 4803 ] Count = 5
2013 - 10 - 25 17 : 06 : 05.617 OperationDemo[ 8562 : 4803 ] Count = 6
2013 - 10 - 25 17 : 06 : 05.618 OperationDemo[ 8562 : 4803 ] Count = 7
2013 - 10 - 25 17 : 06 : 05.618 OperationDemo[ 8562 : 4803 ] Count = 8
2013 - 10 - 25 17 : 06 : 05.619 OperationDemo[ 8562 : 4803 ] Count = 9
2..当我们将操作(NSOperation)添加到操作队列NSOperationQueue之后,操作队列可能立即会执行我们的操作也可能会在以后某个时刻去执行,具体什么时候执行我们是无法控制的。
3.当有多个操作的时候,他们启动执行顺序也是不确定的,但是我们可以绑定操作之间的依赖关系。
self.firstOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(firstOperationEntry:) object:[NSNumber numberWithInteger:111]];
self.secondOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(secondOperationEntry:) object:[NSNumber numberWithInteger:222 ] ];
// [self.firstOperation removeDependency:self.secondOperation] //解除依赖关系
[self.firstOperation addDependency:self.secondOperation];
self.secondOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(secondOperationEntry:) object:[NSNumber numberWithInteger:222 ] ];
// [self.firstOperation removeDependency:self.secondOperation] //解除依赖关系
[self.firstOperation addDependency:self.secondOperation];
使用
addDependency建立依赖关系,这样的话firstOperation会在secondeOperation执行完后再执行,然后我们使用removeDependency可以解除依赖关系。
4.除此之外,我们也可以之间将一个代码块添加到操作队列中去。例如:
[self.operationQueue addOperationWithBlock:^{
NSLog(@"Curent Thread ID:=%@",[NSThread currentThread]);
NSLog(@"######:Hello World");
NSLog(@"Curent Thread ID:=%@",[NSThread currentThread]);
NSLog(@"######:Hello World");
}];
四 将自定义的操作添加到操作队列中
我们需要做一下事情:
a.重写 isConcurrent方法 并返回 YESb.重写 isStart方法 操作队列会调用该方法来执行操作
c.重写 isCancelled方法 操作队列会周期性调用该方法查看当前操作的执行情况
d.重写 main方法,并创建自己的自动回收池,在main函数返回前来释放资源回收池
e.重写 isFinished和isExecuting方法来反馈跟踪操作执行情况
直接上代码:
.h文件:
#import <Foundation/Foundation.h>@interface CountingOperation : NSOperation
@property(nonatomic,assign) BOOL isFinished;
@property(nonatomic,assign) BOOL isExecuting;
-(id) initWithStartingCount:(NSInteger) startCount
endingCount:(NSInteger) end;
@end
.m文件
#import "CountingOperation.h"@interface CountingOperation()
@property(nonatomic,assign) NSInteger startCount;
@property(nonatomic,assign) NSInteger end;
@end
@implementation CountingOperation
@synthesize startCount = _startCount;
@synthesize end = _end;
@synthesize isFinished = _isFinished;
@synthesize isExecuting = _isExecuting;
-(id) init
{
return [self initWithStartingCount:0 endingCount:100];
}
-(id) initWithStartingCount:(NSInteger) startCount
endingCount:(NSInteger) end
{
if (self = [super init])
{
self.startCount = startCount;
self.end = end;
}
return self;
}
//Conflicting return type in implementation of 'start': 'NSInteger' (aka 'int') vs 'void'
- (void) start
{
if ([self isFinished])
{
[self willChangeValueForKey:@"isFinished"];
self.isFinished = YES;
[self didChangeValueForKey:@"isFinished"];
return;
}
else
{
/* If this operation is *not* cancelled */
/* KVO compliance */
[self willChangeValueForKey:@"isExecuting"];
self.isExecuting = YES;
/* Call the main method from inside the start method */
[self didChangeValueForKey:@"isExecuting"];
[self main];
}
}
- (void) main
{
@try {
/* Here is the autorelease pool */
@autoreleasepool
{
NSLog(@"######:Current Thread = %@", [NSThread currentThread]);
/* Keep a local variable here that must get set to YES whenever we are done with the task */
BOOL taskIsFinished = NO;
/* Create a while loop here that only exists
if the taskIsFinished variable is set to YES or the operation has been cancelled */
while (taskIsFinished == NO && [self isCancelled] == NO)
{
/* Perform the task here */
NSLog(@"Main Thread = %@", [NSThread mainThread]);
NSLog(@"Current Thread = %@", [NSThread currentThread]);
NSUInteger counter = self.startCount;
for (counter = self.startCount;counter < self.end;counter++)
{
NSLog(@"Count = %lu", (unsigned long)counter);
}
/* Very important. This way we can get out of the
loop and we are still complying with the cancellation rules of operations */
taskIsFinished = YES;
}
/* KVO compliance. Generate the required KVO notifications */
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
self.isFinished = YES;
self.isExecuting = NO;
[self didChangeValueForKey:@"isFinished"];
[self didChangeValueForKey:@"isExecuting"];
}
}
@catch (NSException * e)
{
NSLog(@"Exception %@", e);
}
}
- (BOOL) isFinished{
/* Simply return the value */
return(_isFinished);
}
- (BOOL) isExecuting{
/* Simply return the value */
return(_isExecuting);
}
- (BOOL) isConcurrent{
return YES;
}
@end
调用方法:
self.customOperation = [[CountingOperation alloc] initWithStartingCount:0 endingCount:10];
[self.operationQueue addOperation:self.customOperation];
输出结果:
2013
-
10
-
25
17
:
06
:
05.607
OperationDemo[
8562
:
4803
] Main Thread = <NSThread:
0x7147910
>{name = (null), num =
1
}2013 - 10 - 25 17 : 06 : 05.607 OperationDemo[ 8562 : 4803 ] Current Thread = <NSThread: 0x717b350 >{name = (null), num = 3 }
2013 - 10 - 25 17 : 06 : 05.608 OperationDemo[ 8562 : 4803 ] Count = 0
2013 - 10 - 25 17 : 06 : 05.616 OperationDemo[ 8562 : 4803 ] Count = 1
2013 - 10 - 25 17 : 06 : 05.616 OperationDemo[ 8562 : 4803 ] Count = 2
2013 - 10 - 25 17 : 06 : 05.617 OperationDemo[ 8562 : 4803 ] Count = 3
2013 - 10 - 25 17 : 06 : 05.617 OperationDemo[ 8562 : 4803 ] Count = 4
2013 - 10 - 25 17 : 06 : 05.617 OperationDemo[ 8562 : 4803 ] Count = 5
2013 - 10 - 25 17 : 06 : 05.617 OperationDemo[ 8562 : 4803 ] Count = 6
2013 - 10 - 25 17 : 06 : 05.618 OperationDemo[ 8562 : 4803 ] Count = 7
2013 - 10 - 25 17 : 06 : 05.618 OperationDemo[ 8562 : 4803 ] Count = 8
2013 - 10 - 25 17 : 06 : 05.619 OperationDemo[ 8562 : 4803 ] Count = 9
注意:
1.在以上的代码中我们使用了KVO机制来,可以让你来观察任务的状态。
2.除此之外,我们也可以挂起和取消所有操作
3.通过使用setMaxConcurrentOperationCount来设置线程池中的线程数,也就是并发操作数。默认情况下是-1,也就是没有限制,同时运行队列中的全部操作。
4.两个操作提交的时间间隔很近,线程池中的线程,谁先启动是不定的
四 总结
NSOperation和NSOperationQueue是在IOS中实现并发执行的方式之一,除此之外我们还可以使用苹果最新推出的GCD技术来更加容易实现并发。关于这两种多线程模型的异同,会在以后的博文中做深入分析。