天天看点

NSOperation的介绍

iOS开发多线程篇—NSOperation简单介绍

一、NSOperation简介 1. 简单说明 a. NSOperation的作用: 是OC语言中基于GCD的面向对象的封装,使用起来比GCD更加简单(面向对象); 提供了一些用GCD不好实现的功能, 使用NSOperation不用关心线程以及线程的生命周期(苹果推荐使用 )。

b. 配合使用 NSOperation和NSOperationQueue实现多线程编程的具体步骤:

(1)先将需要执行的操作封装到一个NSOperation对象中

(2)然后将NSOperation对象添加到NSOperationQueue中

(3)系统会⾃动将NSOperationQueue中的NSOperation取出来

(4)将取出的NSOperation封装的操作放到⼀条新线程中执⾏

c. NSOperation的子类

NSOperation是一个抽象类,不能直接使用(方法没有实现),约束子类都具有共同的属性和方法,并不具备封装操作的能力,必须使⽤它的子类。 使用NSOperation⼦类的方式有3种: (1)NSInvocationOperation (2)NSBlockOperation (3)自定义子类继承NSOperation,实现内部相应的⽅法

2. 具体说明 2.1 NSInvocationOperation子类【 不常用】 创建对象和执行操作:   // 创建操作对象,封装要执行的任务   // NSInvocationOperation 封装操作   NSInvocationOperation *operation=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil]; // 执行操作    [operation start];

注意:操作对象默认在主线程中执行,只有添加到队列中才会开启新的线程。即默认情况下,如果操作没有放到队列中queue中,都是同步执行。只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作,不常用!

2.2 NSBlockOperation子类 注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作。 创建对象和添加操作: // 创建NSBlockOperation操作对象   NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{       NSLog( @" NSBlockOperation1------%@ ",[NSThread currentThread]);            }];   // 通过addExecutionBlock:方法添加更多的操作  [operation addExecutionBlock:^{      NSLog( @" NSBlockOperation2------%@ ",[NSThread currentThread]);            }]; [operation start]; 打印结果: 2015-10-26 14:06:11.765 test1[7036:326837] NSBlockOperation1------<NSThread: 0x7f8628e074d0>{number = 1, name = main} 2015-10-26 14:06:11.767 test1[7036:326882] NSBlockOperation2------<NSThread: 0x7f8628e2ef10>{number = 2, name = (null)}

2.3 NSOperationQueue NSOperation可以调⽤start⽅法来执⾏任务,但默认是同步执行的。 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作。 也就是说添加操作到NSOperationQueue中,自动执行操作,自动开启线程。

添加操作到NSOperationQueue中的两种那个方法:  - (void)addOperation:(NSOperation *)op; - (void)addOperationWithBlock:(void (^)(void))block;

注意:系统自动将NSOperationqueue中的NSOperation对象取出,将其封装的操作放到一条新的线程中执行,这些任务是并行执行的。 提示:队列的取出是有顺序的,与打印结果并不矛盾。这就好比,选手A,B,C虽然起跑的顺序是先A,后B,然后C,但是到达终点的顺序却不一定是A,B在前,C在后。

下面使用for循环打印,可以更明显的看出任务是并发执行的。 代码示例: // 创建 NSInvocationOperation 对象,封装操作

    NSInvocationOperation *operation1=[[ NSInvocationOperation alloc ] initWithTarget : self selector : @selector (test1) object : nil ];

    NSInvocationOperation *operation2=[[ NSInvocationOperation alloc ] initWithTarget : self selector : @selector (test2) object : nil ];

    // 创建 NSBlockOperation 对象,封装操作

    NSBlockOperation *operation3=[ NSBlockOperation blockOperationWithBlock :^{

        for ( int i=0; i<5; i++) {

            NSLog ( @"NSBlockOperation3--1----%@" ,[ NSThread currentThread ]);

        }

    }];

    [operation3 addExecutionBlock :^{

        for ( int i=0; i<5; i++) {

            NSLog ( @"NSBlockOperation3--2----%@" ,[ NSThread currentThread ]);         }     }];

    // 创建 NSOperationQueue

    NSOperationQueue * queue=[[ NSOperationQueue alloc ] init ];

    // 把操作添加到队列中

    [queue addOperation :operation1];

    [queue addOperation :operation2];

    [queue addOperation :operation3]; } -( void )test1

{

    for ( int i=0; i<5; i++) {

        NSLog ( @"NSInvocationOperation1--%@" ,[ NSThread currentThread ]);

    } } -( void )test2

{

    for ( int i=0; i<5; i++) {

        NSLog ( @"NSInvocationOperation2--%@" ,[ NSThread currentThread ]);

    } } 打印结果:

二、NSOperation 和 GCD 的比较 1. GCD a.     GCD是iOS4.0 推出的,主要针对多核cpu做了优化,是C语言的技术; b.     GCD是将任务(block)添加到队列(串行/并行/全局/主队列),并且以同步/异步的方式执行任务的函数; c.     GCD提供了一些NSOperation不具备的功能      1)一次性执行      2)延迟执行      3)调度组

2.  NSOperation a.     NSOperation是iOS2.0推出的,iOS4之后重写了NSOperation; b.     NSOperation将操作(异步的任务)添加到队列(并发队列),就会执行指定的操作; c.     NSOperation里提供了方便的操作: 1)最大并发数:同时执行的任务数。 比如,同时开3个线程执行3个任务,并发数就是3。 最大并发数的相关方法 - (NSInteger)maxConcurrentOperationCount; - (void)setMaxConcurrentOperationCount:(NSInteger)cnt; 示例: -(NSOperationQueue *)queue {

    if(!_queue){

        _queue =[[NSOperationQueue alloc] init];         //设置对大并发数         _queue.maxConcurrentOperationCount = 3;     }

    return _queue; } 2)队列的暂定/继续,; // YES代表暂停队列,NO代表恢复队列。 - (void)setSuspended:(BOOL)b;  - (BOOL)isSuspended; 3)取消队列的所有操作 - (void)cancelAllOperations; 提示:也可以调用NSOperation的- (void)cancel方法取消单个操作。 示例: //暂停,暂停的是队列中还没有被运行的操作,正在运行的操作不能被暂停; //当队列为暂停的时候,往队列中加入操作,也不会运行。 - ( IBAction )pauseClick:( id )sender {

    NSLog ( @" 点击暂停 " );

    [ self . queue setSuspended : YES ]; } // 继续

- ( IBAction )resumeClick:( id )sender {

    NSLog ( @" 点击继续 " );

    [ self . queue setSuspended : NO ]; } // 取消

- ( IBAction )cancelClick:( id )sender {

    NSLog ( @" 点击取消 " );

    [ self . queue cancelAllOperations ]; } 4)指定操作之间的依赖关系(GCD可以用同步实现) a. NSOperation之间可以设置依赖来保证执行顺序,比如一定要让操作A执行完后,才能执行操作B,可以这么写: // 操作B依赖于操作A [operationB addDependency:operationA];  b. 可以在不同queue的NSOperation之间创建依赖关系。 c. 注意不能相互依赖,比如A依赖B,B依赖A。 示例: -( void )demo{     NSBlockOperation *op1 = [ NSBlockOperation blockOperationWithBlock :^{

        // 模拟耗时操作

        [ NSThread sleepForTimeInterval : arc4random_uniform (3)];

        NSLog ( @"1. 登陆 %@" ,[ NSThread currentThread ]);     }];     NSBlockOperation *op2 = [ NSBlockOperation blockOperationWithBlock :^{

        [ NSThread sleepForTimeInterval : arc4random_uniform (3)];

        NSLog ( @"2. 扣费 %@" ,[ NSThread currentThread ]);     }];     NSBlockOperation *op3 = [ NSBlockOperation blockOperationWithBlock :^{

        [ NSThread sleepForTimeInterval : arc4random_uniform (3)];

        NSLog ( @"3. 下载 %@" ,[ NSThread currentThread ]);     }];     NSBlockOperation *op4 = [ NSBlockOperation blockOperationWithBlock :^{

        NSLog ( @"4.UI 更新 %@" ,[ NSThread currentThread ]);     }];     // 添加操作依赖,要在操作放到队列之前。

    // 操作依赖可以跨队列设置。

    [op2 addDependency :op1];

    [op3 addDependency :op2];     [op4 addDependency:op3];     [ self . queue addOperations :@[op1,op2,op3] waitUntilFinished : NO ];

    [[ NSOperationQueue mainQueue ] addOperation :op4]; } 5)操作队列的优先级 设置NSOperation在queue中的优先级,可以改变操作的执行优先级。 操作的优先级,优先级高的操作不一定最先运行。 队列的优先级 ,优先级高的队列不一定比优先级低的队列先运行完,而是有更多的可能被执行到。

6)线程间通信: 子线程->主线程  操作方式与GCD类似。 示例: [self.queue addOperationWithBlock:^{          //子线程: do something     [[NSOperationQueue mainQueue] addOperationWithBlock:^{          //主线程的任务,操作UI等     }]; }];

此外: 有些操作任务如果在主线程完成,会严重的影响到用户体验,造成UI卡的现象。我们可以通过自定义NSOperation,新开线程,让加载图片的任务异步执行,来解决此问题。

继续阅读