• 前言

    这篇文章主要讲NSOperation的使用。

  • What

    使用NSOperation和NSOperationQueue进行多线程开发类似于线程池,只要将一个NSOperation(实际开发中需要使用其子类NSInvocationOperation、NSBlockOperation)放到NSOperationQueue这个队列中线程就会依次启动。NSOperationQueue负责管理、执行所有的NSOperation,在这个过程中可以更加容易管理线程总数和控制线程之间的依赖关系。

    NSOperation 利用他来创建线程操作,线程操作只有放在线程队列中才会在子线程中执行。

    NSOperationQueue: 线程队列分两种类型。

    • 主队列

      • [NSOperationQueue mainQueue]
      • 凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行。
    • 非主队列
      • [[NSOperationQueue alloc]init]
      • 添加到这种队列中的任务,都会放到子线程中执行。

    NSOperation常用子类用于创建线程操作:NSInvocationOperation和NSBlockOperation,两种方式本质没有区别,但后者使用block形式进行代码组织,使用相对方便。也可以用自定义的继承于NSOperation的类来创建线程操作。

  • How

    配合使用NSOperation和NSOperationQueue实现多线程编程,一共有三种方式,但其实这三种方式都是采用NSOperation的子类与NSOperationQueue搭配实现多线程开发。这三个子类分别是NSInvocationOperation、NSBlockOperation和自定义继承于NSOperation的类,前两者是系统提供的子类。

    方式一 NSInvocationOperation与NSOperationQueue搭配

    1. 创建一个线程操作,并实现方法选择器选择的方法

      //创建一个线程操作
      NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(invocationOperation:) object:kurl];
      
      //让线程操作开始执行。但是如果这样做的话这个操作将会在主线程中执行,只有将这个操作放进队列,才会开辟一个子线程让这个操作在子线程中执行。
      //[invocationOperation start];
    2. 创建一个线程队列

      NSOperationQueue *operationQueue = [NSOperationQueue new];
    3. 将创建好的线程操作放在线程队列中

      //只有放在线程队列中的线程操作才会在子线程中执行。线程队列负责管理、执行所有的NSOperation
      [operationQueue addOperation:invocationOperation];
    4. 在创建线程操作时选择的方法内更新UI

      - (void)invocationOperation:(NSString *)url{
          //虽然没有采用NSThread创建线程,但仍可以使用[NSThread currentThread]来获取当前的线程。
          NSLog(@"invocationOperation方法所在的线程%@",[NSThread currentThread]);
      
          NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kurl]];
      
          //在子线程中回到主线程更新UI
          [[NSOperationQueue mainQueue] addOperationWithBlock:^{
      
              imageView.image = [UIImage imageWithData:data];
      
          }];
      
      }

      到目前为止是不是感觉跟NSThread挺相似的,接下来简单分析一下两者的区别。

      1. NSThread需要启动,也就是说需要费心管理声明周期,而采用Operation只需将线程操作放到线程队列中即可,线程队列负责管理、执行所有的NSOperation。

      2. 管理线程的最大并发数,也就是同时执行的任务数。

        //默认是-1,不能设为0,如果设置为0就不执行任务。
        operationQueue.maxConcurrentOperationCount = 1;
      3. 控制线程之间的依赖关系,NSOperation之间可以设置依赖来保证执行顺序,比如一定要让操作1执行完后,才能执行操作2。线程之间不能相互依赖,不如A依赖于B,B有依赖于A。

        //操作1依赖于操作2
        [invocationOperation1 addDependency:invocationOperation2];
      4. 队列的取消、暂停、恢复

        • 只要设置队列的suspended为YES, 那么就会暂停队列中其它任务的执行,也就是说不会再继续执行没有执行到得任务

          self.queue.suspended = YES;

          注意: 设置为暂停之后, 不会立即暂停,会继续执行当前正在执行的任务, 直到当前任务执行完毕, 就不会执行下一个任务了,也就是说, 暂停其实是暂停下一个任务, 而不能暂停当前任务
          注意: 暂停是可以恢复的,只要设置队列的suspended为NO, 那么就会恢复队列中其它任务的执行

        • 取消队列中所有的任务的执行

          [self.queue cancelAllOperations];

          取消和暂停一样, 是取消后面的任务, 不能取消当前正在执行的任务,取消是不可以恢复的

    方式二 NSBlockOperation与NSOperationQueue搭配,其实方式一和方式二没有什么本质区别。主要是后者使用block形式进行代码组织,使用相对方便。

    - (void)viewDidLoad{
         [super viewDidLoad];
    
            imageView = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 200, 200)];
            [self.view addSubview:imageView];
    
            //1. 创建线程操作
            NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    
                NSLog(@"blockOperation线程操作所在的线程%@",[NSThread currentThread]);
    
                NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kurl]];
    
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
    
                NSLog(@"更新UI所在的线程%@",[NSThread currentThread]);
    
                imageView.image = [UIImage imageWithData:data];
    
                }];
            }];
            //2. 创建线程队列
            NSOperationQueue *operationQueue = [NSOperationQueue new];
            //3. 将线程操作放到线程队列中
            [operationQueue addOperation:blockOperation];
    
            //PS: 简化以上操作
            //1. 创建一个线程队列
            NSOperationQueue *operationQueue = [NSOperationQueue new];
    
           //直接利用线程队列的addOperationWithBlock添加线程操作。
           [operationQueue addOperationWithBlock:^{
    
              NSLog(@"更新UI所在的线程%@",[NSThread currentThread]);
    
           }];
    
    }

    方式三 继承于NSOperation的子类与NSOperationQueue的搭配

    1. 创建一个继承于NSOperation的类,并在.m文件中重写main方法,main方法便是该线程要执行的操作。注意,如果是同步操作,该方法能够自动访问到主线程的自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池,需要再main中再新建一个自动释放池,来帮助管理内存。

    2. 创建线程队列,并把线程操作放在线程队列中。

      .h
      //
      //  CoustomOperation.h
      //  NSOperation
      //
      //  Created by GG on 16/2/26.
      //  Copyright © 2016年 GG. All rights reserved.
      //
      
      #import <Foundation/Foundation.h>
      #import <UIKit/UIKit.h>
      @interface CoustomOperation : NSOperation
      
      //接收传进来的图片对象
      @property (nonatomic,retain) UIImageView *imageView;
      
      //在该该类对象初始化时,将图片试图对象传到类中。
      - (instancetype)initWithImageView:(UIImageView *)imageView;
      
      @end
      .m
      //
      //  CoustomOperation.m
      //  NSOperation
      //
      //  Created by GG on 16/2/26.
      //  Copyright © 2016年 GG. All rights reserved.
      //
      
      #import "CoustomOperation.h"
      
      #define kurl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943"
      
      @implementation CoustomOperation
      
      - (instancetype)initWithImageView:(UIImageView *)imageView
      {
           self = [super init];
           if (self) {
      
              self.imageView = imageView;
           }
           return self;
      }
      
      - (void)main{
      
            //新建一个自动释放池,因为如果是同步操作,该方法能够自动访问到主线程的自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池。
            @autoreleasepool {
      
                   NSLog(@"获取图片所在的线程%@",[NSThread currentThread]);
                   NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:kurl]];
      
                   [[NSOperationQueue mainQueue] addOperationWithBlock:^{
      
                   NSLog(@"更新UI所在的线程%@",[NSThread currentThread]);
      
                   self.imageView.image = [UIImage imageWithData:imageData];
      
                   }];
      
             }
      }
      
      @end
      ViewController.m
      - (void)viewDidLoad{
            [super viewDidLoad];
      
            imageView = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 200, 200)];
            [self.view addSubview:imageView];
      
           CoustomOperation *coustomOperation = [[CoustomOperation alloc] initWithImageView:(UIImageView *)imageView];
      
           NSOperationQueue *operationQueue = [NSOperationQueue new];
          //
          [operationQueue addOperation:coustomOperation];
      
      }

总结: 这三种方式中,感觉方式一是最麻烦的,方式二相对而已简洁不少,而方式三更适合于封装某一个线程操作。

iOS-多线程之NSOperation的更多相关文章

  1. iOS多线程之NSOperation详解

    使用NSOperation和NSOperationQueue进行多线程开发,只要将一个NSOperation(实际开发中需要使用其子类 NSInvocationOperation,NSBlockOpe ...

  2. iOS 多线程之NSOperation篇举例详解

    这篇博客是接着总篇iOS GCD NSOperation NSThread等多线程各种举例详解写的一个支篇.总篇也包含了此文的链接.本文讲解的知识点有NSBlockOperationClick,队列, ...

  3. IOS多线程之NSOperation学习总结

    NSOperation简介 1.NSOperation的作用 配合使用NSOperation和NSOperationQueue也能实现多线程编程 2.NSOperation和NSOperationQu ...

  4. iOS多线程之NSOperation,NSOperationQueue

    使用 NSOperation的方式有两种, 一种是用定义好的两个子类: NSInvocationOperation 和 NSBlockOperation. 另一种是继承NSOperation 如果你也 ...

  5. (五十六)iOS多线程之NSOperation

    NSOpertation是一套OC的API,是对GCD进行的Cocoa抽象. NSOperation有两种不同类型的队列,主队列和自定义队列. 主队列运行于主线程上,自定义队列在后台运行. [NSBl ...

  6. iOS多线程之NSOperation和NSOperationQueue的使用

    一:NSOperation 两个子类+重写main方法 NSInvocationOperation NSBlockOperation 有个类方法 BlockOprationWith: 还有就是自己个子 ...

  7. ios多线程之NSOperation

    使用 NSOperation的方式有两种, 一种是用定义好的两个子类: NSInvocationOperation 和 NSBlockOperation. 另一种是继承NSOperation 如果你也 ...

  8. iOS多线程之8.NSOPeration的其他用法

      本文主要对NSOPeration的一些重点属性和方法做出介绍,以便大家可以更好的使用NSOPeration. 1.添加依赖 - (void)addDependency:(NSOperation * ...

  9. iOS多线程之GCD小记

    iOS多线程之GCD小记 iOS多线程方案简介 从各种资料中了解到,iOS中目前有4套多线程的方案,分别是下列4中: 1.Pthreads 这是一套可以在很多操作系统上通用的多线程API,是基于C语言 ...

  10. 多线程之NSOperation

    关于多线程会有一系列如下:多线程之概念解析 多线程之pthread, NSThread, NSOperation, GCD 多线程之NSThread 多线程之NSOperation 多线程之GCD

随机推荐

  1. 前端们,gulp该用起来了,简单的demo入门——gulp系列(一)

    gulp.grunt前端自动化工具,只有用过才知道多么重要. 它们问世这么久了?你真的用过吗? 写个简单的less.watch任务的demo分享———— 1.准备: 安装全局node.npm,这个教程 ...

  2. The Top 10 Javascript MVC Frameworks Reviewed

    Over the last several months I have been in a constant search for the perfect javascript MVC framewo ...

  3. JS&CSS文件请求合并及压缩处理研究(二)

    上篇交待了一些理论方面的东西,并给出了另外一种解决方案的处理流程.本篇将根据该处理流程,开始代码方面的编写工作. 1,打开VS,新建ASP.NET MVC Web项目,项目类型选择空.名称为 Mcmu ...

  4. MvvmLight框架使用入门(一)

    MvvmLight是比较流行的MVVM框架,相对较为简单易用.可能正因为简单,对应的帮助文档不多,对初学者就不够友好了.这里会用几篇随笔,就个人对MvvmLight的使用经验,来做一个入门的介绍. 第 ...

  5. servlet中的细节

    Get方法有大小限制:1024个字符.这些信息使用 Query_String头传递,并通过Query_String环境变量访问.Post方法:请求体信息使用FromData头传递.读取所有表单参数:g ...

  6. 搜索 + 剪枝 --- POJ 1101 : Sticks

    Sticks Problem's Link:   http://poj.org/problem?id=1011 Mean: http://poj.org/problem?id=1011&lan ...

  7. C#-Windows服務以LocalSystem賬戶安裝的話無法獲取我的文檔路徑

    如圖,如果Window服務以上圖 Account安裝運行,則無法獲取到 以下路徑: System.Environment.GetFolderPath(Environment.SpecialFolder ...

  8. 【循序渐进学Python】4. Python中的序列——字典

    字典是Python内建的六种序列之一.字典作为一种常用的数据结构,字典中的值没有特定顺序,每个值都对应于一个唯一的键.键可以是数字.字符串甚至是元组. 1. 创建和使用字典 Python中字典可以使用 ...

  9. 初识Windows窗体(包括各种控件,属性,方法)

    什么是Wind ows窗体? 顾名思义,win dows窗体就是将一些所必须的信息通过窗体的形式展示给客户看.例如:我们经常玩的QQ登陆界面,微信登陆界面,等等,都是以窗体的形式将信息展示给我们看的. ...

  10. C# 生成XML空元素/空节点自动换行解决方案

    使用DataSet可以直接输出XML,并可指定是否带有Schema: ds.WriteXml(XMLFile,XmlWriteMode.WriteSchema ) 不过,这样将不会输出值为Null的字 ...