NSOperation与NSOprationQueue

本文介绍NSOperation和NSOperationQueue的基本概念及使用方法,包括如何利用NSInvocationOperation和NSBlockOperation实现多线程编程,以及自定义NSOperation子类进行更复杂的任务处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NSOperation的作用

  • 配合使用NSOperation和NSOperationQueue也能实现多线程编程

NSOperation和NSOperationQueue实现多线程的具体步骤

  • 先将需要执行的操作封装到一个NSOperation对象中
  • 然后将NSOperation对象添加到NSOperationQueue中
  • 系统会自动将NSOperationQueue中的NSOperation取出来
  • 将取出的NSOperation封装的操作放到一条新线程中执行

NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类

  • 使用NSOperation子类的方式有3种:
  • NSInvocationOperation
  • NSBlockOperation
  • 自定义子类继承NSOperation,实现内部相应的方法

NSInvocationOperation

  • 创建NSInvocationOperation对象
     - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
  • 调用start方法开始执行操作
      - (void)start;

一旦执行操作,就会调用target的sel方法

  • 注意:
  • 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
  • 只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
  • 操作
      - (void)invocation
{
NSInvocationOperation*op1=[[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(demo)object:nil];
 //如果直接执行NSInvocationOperation中的操作,那么默认会在主线程中执行
  [op1 start];
  NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
  [op2 start];
}

NSBlockOperation

  • 创建NSBlockOperation对象
      + (id)blockOperationWithBlock:(void (^)(void))block;
  • 通过addExecutionBlock:方法添加更多的操作
     - (void)addExecutionBlock:(void (^)(void))block;
  • 注意:
    只要NSBlockOperation封装的操作数 > 1,就会异步执行操作

  • 使用
     - (void)blockOperation
 {
    NSBlockOperation * op1=[NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create----%@",[NSThread currentThread]);
    }];
    [op1 addExecutionBlock:^{
        NSLog(@"add1-----%@",[NSThread currentThread]);
    }];
    [op1 start];

    //如果只封装了一个操作, 那么默认会在主线程中执行
    //如果封装了多个操作, 那么除了第一个操作以外, 其它的操作会在子线程中执行
}
output:
2016-06-27 16:04:52.512 test1[9640:2351301] create----<NSThread: 0x7fea43e008f0>{number = 1, name = main}
2016-06-27 16:04:52.512 test1[9640:2351344] add1-----<NSThread: 0x7fea43d19480>{number = 2, name = (null)}

GCD的队列类型

  • 并发队列:自己创建,全局
  • 串行队列:主队列自己创建,自己创建的

NSOperationQueue

  • 自己创建: alloc/init --> 默认是并发 --> 也可以让它串行
  • 主队列 : mainQueue
  • NSOperationQueue的作用
  • NSOperation可以调用start方法来执行任务,但默认是同步执行的
  • 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作

  • 添加操作到NSOperationQueue中
      - (void)addOperation:(NSOperation *)op;
      -  (void)addOperationWithBlock:(void (^)(void))block
  • 使用
     - (void)block
{
    NSOperationQueue * queue=[[NSOperationQueue alloc]init];
    [queue addOperationWithBlock:^{
        NSLog(@"block1----%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"block2----%@",[NSThread currentThread]);
    }];
}
output:
2016-06-27 16:35:30.415 test1[9859:2380633] block2----<NSThread: 0x7f88f1f13550>{number = 3, name = (null)}
2016-06-27 16:35:30.415 test1[9859:2380610] block1----<NSThread: 0x7f88f1d0f530>{number = 2, name = (null)}
    - (void)invation
{
    //创建两个操作
    NSInvocationOperation * op1=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test1) object:nil];
     NSInvocationOperation * op2=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test2) object:nil];
    //创建一个队列,如果创建的是mainQueue那么就是在主队列中执行,不然就是在并行队列中执行
    NSOperationQueue * queue=[[NSOperationQueue alloc]init];
    [queue addOperation:op1];
    [queue addOperation:op2];

}
- (void)test1
{
    for (int i=0; i<5; i++) {
        NSLog(@"test1---%@",[NSThread currentThread]);
    }
}
- (void)test2
{
    for (int i=0; i<5; i++) {
       NSLog(@"test2---%@",[NSThread currentThread]);
    }
  }
output:
2016-06-27 16:55:05.804 test1[9937:2389981] test2---<NSThread: 0x7f9ee062d150>{number = 3, name = (null)}
2016-06-27 16:55:05.804 test1[9937:2389978] test1---<NSThread: 0x7f9ee0704a20>{number = 2, name = (null)}
2016-06-27 16:55:05.806 test1[9937:2389978] test1---<NSThread: 0x7f9ee0704a20>{number = 2, name = (null)}
2016-06-27 16:55:05.806 test1[9937:2389981] test2---<NSThread: 0x7f9ee062d150>{number = 3, name = (null)}
2016-06-27 16:55:05.807 test1[9937:2389978] test1---<NSThread: 0x7f9ee0704a20>{number = 2, name = (null)}
2016-06-27 16:55:05.807 test1[9937:2389981] test2---<NSThread: 0x7f9ee062d150>{number = 3, name = (null)}
2016-06-27 16:55:05.807 test1[9937:2389978] test1---<NSThread: 0x7f9ee0704a20>{number = 2, name = (null)}
2016-06-27 16:55:05.808 test1[9937:2389981] test2---<NSThread: 0x7f9ee062d150>{number = 3, name = (null)}
2016-06-27 16:55:05.808 test1[9937:2389978] test1---<NSThread: 0x7f9ee0704a20>{number = 2, name = (null)}
2016-06-27 16:55:05.808 test1[9937:2389981] test2---<NSThread: 0x7f9ee062d150>{number = 3, name = (null)}

最大并发数

  • 什么是并发数
  • 同时执行的任务数
    比如,同时开3个线程执行3个任务,并发数就是3

  • 最大并发数的相关方法
     - (NSInteger)maxConcurrentOperationCount;
     -  (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

队列的取消、暂停、恢复

  • 取消队列的所有操作
     - (void)cancelAllOperations;
  • 提示:也可以调用NSOperation的- (void)cancel方法取消单个操作

  • 暂停和恢复队列

      -  (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
      - (BOOL)isSuspended;

操作依赖

  • NSOperation之间可以设置依赖来保证执行顺序
  • 比如一定要让操作A执行完后,才能执行操作B,可以这么写
[operationB addDependency:operationA]; // 操作B依赖于操作A
  • 使用,使用两个图片拼接这种典型
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self setUp];
}
- (void)setUp
{
    __block UIImage *image1=nil;
    __block UIImage *image2=nil;
    //创建一个异步队列
    NSOperationQueue *queue=[[NSOperationQueue alloc]init];
    //开启一个子线程下载图片
    NSOperation *op1=[NSBlockOperation blockOperationWithBlock:^{
        NSURL *url=[NSURL URLWithString:@"http://s.wasu.cn/data/images/201604/13/570e0b67c15fb.jpg"];
        NSData *date=[NSData dataWithContentsOfURL:url];
        image1=[UIImage imageWithData:date];
        NSLog(@"---op1----%@",[NSThread currentThread]);
    }];
    //下载另一个图片
    NSOperation *op2=[NSBlockOperation blockOperationWithBlock:^{
        NSURL *url=[NSURL URLWithString:@"http://i1.hdslb.com/bfs/archive/5e91addadc849572575b592814846977ab5559f1.jpg"];
        NSData *date=[NSData dataWithContentsOfURL:url];
        image2=[UIImage imageWithData:date];
        NSLog(@"---op2----%@",[NSThread currentThread]);
    }];

    //拼接图片
    NSOperation *op3=[NSBlockOperation blockOperationWithBlock:^{
        CGSize size=CGSizeMake(300, 400);
        UIGraphicsBeginImageContext(size);
        [image1 drawAsPatternInRect:CGRectMake(0, 0, 150,400)];
        [image2 drawAsPatternInRect:CGRectMake(150, 0, 150, 400)];
        UIImage * image=UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            NSLog(@"进入主线程");
            self.imageView.image=image;
        }];
        NSLog(@"---op3----%@",[NSThread currentThread]);

    }];
    op1.completionBlock=^{
        NSLog(@"第一张图下载完毕");
    };
    op2.completionBlock=^{
        NSLog(@"第二张图下载完毕");
    };

    //依赖要放在添加队列之前
    [op3 addDependency:op1];
    [op3 addDependency:op2];
    NSArray *opreations=[NSArray arrayWithObjects:op1,op2,op3, nil];
    [queue addOperations:opreations waitUntilFinished:NO];
}
@end
output:
2016-06-27 17:35:39.651 图片合成[10336:2417737] ---op2----<NSThread: 0x7fe702c4c4e0>{number = 3, name = (null)}
2016-06-27 17:35:39.651 图片合成[10336:2417736] 第二张图下载完毕
2016-06-27 17:35:40.528 图片合成[10336:2417738] ---op1----<NSThread: 0x7fe702e80fa0>{number = 4, name = (null)}
2016-06-27 17:35:40.529 图片合成[10336:2417781] 第一张图下载完毕
2016-06-27 17:35:40.552 图片合成[10336:2417666] 进入主线程
2016-06-27 17:35:40.552 图片合成[10336:2417737] ---op3----<NSThread: 0x7fe702c4c4e0>{number = 3, name = (null)}
  • 可以在不同queue的NSOperation之间创建依赖关系

操作的监听

  • 可以监听一个操作的执行完毕
      -  (void (^)(void))completionBlock;
      -  (void)setCompletionBlock:(void (^)(void))block;

自定义NSOperation

  • 自定义NSOperation的步骤很简单
  • 重写- (void)main方法,在里面实现想执行的任务

  • 重写- (void)main方法的注意点
  • 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
  • 经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应

转载于:https://www.cnblogs.com/kissshot13/p/5620907.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值