IOS NSOpertation和NSOperationQueue的使用

一 NSOperation简介
    
        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:) 
                                                                  object:simpleObject];
    [
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.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: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)到队列中。
看下面这段代码:

@interface WBViewController ()
@property (nonatomic, strong) NSOperationQueue *operationQueue;
@property (nonatomic, strong) NSInvocationOperation *firstOperation;
@property (nonatomic, strong) NSInvocationOperation *secondOperation;
@end

@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] ];
   
   [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的线程中运行,从而实现了并行执行
           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];
使用 addDependency建立依赖关系,这样的话firstOperation会在secondeOperation执行完后再执行,然后我们使用removeDependency可以解除依赖关系
           
         4.除此之外,我们也可以之间将一个代码块添加到操作队列中去。例如

       [self.operationQueue addOperationWithBlock:^{
           
 NSLog(@"Curent Thread ID:=%@",[NSThread currentThread]);
           
 NSLog(@"######:Hello World");
       }];

四 将自定义的操作添加到操作队列中

我们需要做一下事情:
             a.重写 isConcurrent方法  并返回 YES
            b.
重写 isStart方法           操作队列会调用该方法来执行操作
            c.
重写 isCancelled方法    操作队列会周期性调用该方法查看当前操作的执行情况
            d.
重写 main方法,并创建自己的自动回收池,在main函数返回前来释放资源回收池
            e.
重写 isFinishedisExecuting方法来反馈跟踪操作执行情况



直接上代码:
.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技术来更加容易实现并发。关于这两种多线程模型的异同,会在以后的博文中做深入分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值