认识和使用NSOperation

作者: CoderAO | 来源:发表于2015-07-12 22:41 被阅读6226次

    NSOperation是OC中多线程技术的一种,是对GCD的OC包装.它包含队列(NSOperationQueue)操作(NSOperation)两个基本要素.

    通过这篇文章你可以了解到:

    • 怎样使用NSOperation
    • 怎样使用NSOperationQueue
    • 如何给NSOperationQueue设置并发数
    • NSOperationQueue的暂停恢复和取消
    • 通过添加依赖影响操作的执行顺序
    • NSOperation的进程间通信

    怎样使用NSOperation

    • NSOperation本身是一个抽象类,要使用可以通过以下几个办法:
      • 使用NSInvocationOperation
      • 使用NSBlockOperation
      • 自定义NSOperation的子类

    使用

    • NSInvocationOperation
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];
    // 调用start方法执行操作op操作
    [op start];
    
    • NSBlockOperation
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task0---%@", [NSThread currentThread]);
    }];
    [op start];
    

    根据打印的结果我们会发现,直接调用start方法时,系统并不会开辟一个新的线程去执行任务,任务会在当前线程同步执行.

    注意: 这里我们说的是当前线程而非主线程,意即:如果是在主线程中调用op的start方法,那么该任务是在主线程中执行;但如果是在其他子线程调用start方法,任务则是在其他子线程执行.

    当然NSBlockOperation还有一种使用方法addExecutionBlock:使得我们可以给其添加更多的操作,使用如下:

    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task0---%@", [NSThread currentThread]);
    }];
    
    [op addExecutionBlock:^{
        NSLog(@"task1----%@", [NSThread currentThread]);
    }];
    
    [op addExecutionBlock:^{
        NSLog(@"task2----%@", [NSThread currentThread]);
    }];
    
    // 开始必须在添加其他操作之后
    [op start];
    
    

    由打印的结果发现: task0的结果和前面的结论一样,是执行在主线程中的(因为是在主线程中调用start方法),但task1和task2都是在自己的新线程中执行.也就是说:当NSBlockOperation封装的操作数大于1的时候,就会执行异步操作.

    • 自定义NSOperation

    自定义NSOperation的方法也很简单,我们需要做到下面几个步骤:

    1.子类化NSOperation

    2.在.m文件里面实现-(void)main方法

    3.初始化该操作的时候直接调用alloc及init即可

    4.同样可以通过start方法让你自定义的任务跑在当前线程中

    关键代码:

    自定义NSOperation的实现文件:

    #import "AROperation.h"
    @implementation AROperation
    - (void)main {
        NSLog(@"this is my custom operation ---- %@", [NSThread currentThread]);
    }
    @end
    

    使用(在任何需要启用该任务的地方):

    // 该操作被执行时就会执行op内部定义的任务
    AROperation *op = [[AROperation alloc] init];
    [op start];
    

    怎样使用NSOperationQueue

    NSOperation的start方法默认是同步执行任务,这样的使用并不多见,只有将NSOperation与NSOperationQueue进行结合,才会发挥出这种多线程技术的最大功效.当NSOperation被添加到NSOperationQueue中后,就会全自动地执行异步操作.

    • NSOperationQueue的种类:

      • 自带主队列[NSOperationQueue mainQueue]: 添加到主队列中的任务都会在主线程中执行
      • 自己创建队列(非主队列)
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];: 这种队列同时包含串行、并发的功能,添加到非主队列的任务会自动放到子线程中执行
    • 向NSOperationQueue中添加操作:

      • 直接添加
        [queue addOperation:op1];
      • 使用block添加,block的内容会被包装成operation对象添加到队列
        [queue addOperationWithBlock:^{ }];
        操作一但被到添加到队列中,就会自动异步执行.

    设置NSOperationQueue的最大并发数

    NSOperationQueue可以通过以下方法设置最大并发数,
    setMaxConcurrentOperationCount:,值得注意的是:**当并发数为1就变成了串行执行任务
    **

    NSOperationQueue的暂停恢复和取消

    • 取消
      • NSOperation有一个cancel方法可以取消单个操作
      • NSOperationQueue的cancelAllOperations相当于队列中的每个operation调用了cancel方法,会取消队列里面全部的操作.
      • 但是,不能取消正在进行中的任务,队列调用了cancelAllOperations后会等当前正在进行的任务执行完闭后取消后面的操作
    • 挂起和恢复
      • isSuspended : 判断是否挂起
      • setSuspended: YES表示挂起,NO表示恢复
      • 和取消功能类似,我们同样不能挂起正在运行中的操作,队列会等当前操作结束后将后面的操作暂停(挂起)

    因此, 我们在自定义NSOperation的时候需要注意,最好可以经常通过判断isCancelled方法检测操作是否被取消,以响应外部可能进行的取消操作.如:

    // 自定义NSOperation类.m文件的main方法实现
    - (void)main {
        for (NSInteger i = 0; i < 1000; i++) {
            NSLog(@"SubTask---0---%zd",i);
        }
        // 判断当前任务是否被取消,如果已经取消,及时返回
        if (self.cancelled) {
            return;
        }
    
        for (NSInteger i = 0; i < 1000; i++) {
    
            NSLog(@"SubTask---1---%zd", i);
        }
        if (self.cancelled) {
            return;
        }
    
        for (NSInteger i = 0; i < 1000; i++) {
            NSLog(@"SubTask---2---%zd",i);
        }
    }
    

    添加依赖和监听

    • 通过设置操作间的依赖,可以确定这些操作的执行顺序
      如:
    [op3 addDependency:op1];
    [op3 addDependency:op2];
    
    表示op3会在op1和op2都执行完毕后才执行
    

    添加依赖的时候要注意防止添加循环依赖,此外我们还可以在不同队列的operation之间添加依赖

    • 监听
      • op.completeBlock可以监听一个操作执行完毕的时刻,这个block里面可以添加一些我们需要执行的操作
      • 这个block里面的操作仍然是在子线程执行,但不一定和被监听的操作在同一个线程

    线程间通信

    有时我们在子线程中执行完一些操作的时候,需要回到主线程做一些事情(如进行UI操作),因此需要从当前线程回到主线程,以下载并显示图片为例,方法如下:

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 子线程下载图片
    [queue addOperationWithBlock:^{
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [[UIImage alloc] initWithData:data];
        // 回到主线程进行显示
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    
    

    NSOperation的使用还是比较简单的,但是要注意的细节比较多,一些方法比较容易被忽略,于是乎特此总结一下.这里就不上传示例代码了,有问题的童鞋可以直接留言.

    相关文章

      网友评论

      • bff9ad620fbe:在自定义NSOperation的main函数中应该创建autorealeasepool,不然会造成内存泄漏
      • 小凡凡520:good mark

      本文标题:认识和使用NSOperation

      本文链接:https://www.haomeiwen.com/subject/ryeuqttx.html