NSOperation与NSOperationQueue

上一篇文章为大家简单介绍了NSThread与GCD,并且还对它们进行了一个简单的比对,本篇文章将着重介绍NSOperation与NSOperationQueue,如果有写的不对的地方欢迎大家拍砖或者交流。

NSOperation与GCD

如标题,GCD与NSOperation是iOS目前比较先进也比较好用的两套多线程的API。先对二者做一个比较:

  • GCD:是基于C的底层而实现的,是一种轻量级的多线程变成的API,不需要计划任务单位,但是对于线程之间添加依赖关系会比较繁杂。
  • NSOperation:NSOperation是基于GCD实现的,是对GCD的一种封装,当然也就比GCD多了一些系统开销,但是可以在多个操作中添加附属,可以重用、暂停或者取消,可以轻易实现一些GCD需要花费大量代码去完成的事情。
    以上是二者之间最本质的区别,当然实际使用需要看各自的应用场景,并不是越高级别抽象的越好。
    相比较于GCD,NSOperation实现多线程就需要稍微多一些步骤。GCD的任务都可以写在其block中,没有太过复杂的操作,但是对于线程暂停等操作,GCD实现起来会比较麻烦。
    而NSOperation实现多线的步骤是这样的:
  • 把需要执行的操作封装到一个NSOperation对象中
  • 把NSOperation对象添加到NSOperationQueue中
  • 系统自动提取NSOperation与NSOperationQueue
  • 将提取出来的NSOperation封装的操作放到一条新的线程中执行

NSOperation

NSOperation是一个抽象类,不能够封装操作,只能通过以下三种方式来实现封装操作:

  • NSInvocationOperation
  • NSBlockOperation
  • 自定义子类继承NSOperation,实现内部的相应方法(重写main或start方法来定义自己的operation)

NSInvocationOperation

//创建NSInvocationOperation对象
    NSInvocationOperation *invoOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(doSomeThing) object:nil];
//调用start方法来执行操作
    [invoOperation start];

NSInvocationOperation比较简单,继承了NSOperation,它是基于一个对象和selector来创建操作,可以直接使用而不需继承来实现自己的操作处理。并且默认情况下,调用了start方法之后不会开一条新线程去操作,是在当前线程下同步执行操作,只有将其放到NSOperationQueue中才会执行异步操作。

NSBlockOperation

//创建NSBlockOperation对象
    NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create1----%@",[NSThread currentThread]);
    }];

    //通过addExecutionBlock:方法添加更多的操作
    [blockOperation1 addExecutionBlock:^{
        NSLog(@"add1-----%@",[NSThread currentThread]);
    }];
    
    [blockOperation1 addExecutionBlock:^{
        NSLog(@"add2-----%@",[NSThread currentThread]);
    }];

    //创建NSBlockOperation对象
    NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create2----%@",[NSThread currentThread]);
    }];
    
    [blockOperation2 addExecutionBlock:^{
        NSLog(@"add2--1-----%@",[NSThread currentThread]);
    }];
    
    [blockOperation2 addExecutionBlock:^{
        NSLog(@"add2--2-----%@",[NSThread currentThread]);
    }];
    
    [blockOperation1 start];
    [blockOperation2 start];

打印结果:

NSBlockOperation.png

可以看出NSBlockOperation如果封装了多个操作,那么除了第一个操作外,其他的操作会在子线程中进行。如果只封装了一个操作,默认在主线程中进行,并且只要NSBlockOperation封装的操作数大于一个,就会异步执行操作。

NSOperationQueue

NSOperationQueue可以通过alloc/init来自己创建,默认是并发执行,也可以让它串行。如果将NSOperation添加到NSOperationQueue中,系统会自动异步执行NSOperation中的操作。可以将NSOperationQueue看作是一个线程池,可以向其中添加操作。

NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create1----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create2----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create3----%@",[NSThread currentThread]);
    }];
    
    NSOperationQueue *myQueue = [[NSOperationQueue alloc]init];
    //设置队列名字
    myQueue.name = @"MyQueue";
    //设置最大并发数量(同事开3个线程执行3个任务)
    myQueue.maxConcurrentOperationCount = 3;
    
    //添加操作到NSOperationQueue
    [myQueue addOperation:blockOperation1];
    [myQueue addOperation:blockOperation2];
    [myQueue addOperation:blockOperation3];
    [myQueue addOperationWithBlock:^{
        NSLog(@"create4----%@",[NSThread currentThread]);
    }];

最大并发操作数量:
队列中最多同时运行几条线程。虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue
执行两次打印结果:

NSOperationQueue.png

队列的相关操作

取消

//取消队列的所有操作
[myQueue cancelAllOperations];

打印结果:无打印
//取消某一个操作

 [blockOperation2 cancel];

打印结果:

cancel.png

暂停和恢复

//YES代表暂停队列,NO代表恢复队列
if (a % 2 == 0) {
        [myQueue setSuspended:YES];
        //或
        //myQueue.isSuspended = YES;
    }else{
        [myQueue setSuspended:NO];
    }

操作依赖

NSOperation之间可以设置依赖来保证执行顺序,当某个NSOperation对象依赖于其他NSOperation对象的时候,就可以通过addDependency方法添加一个或多个依赖对象,只有所有依赖的对象完成操作之后,当前的NSOperation对象才会开始操作,还可以通过removeDependency来删除依赖对象。

NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create1----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create2----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create3----%@",[NSThread currentThread]);
    }];
    
    NSOperationQueue *myQueue = [[NSOperationQueue alloc]init];
    //设置队列名字
    myQueue.name = @"MyQueue";
    //设置最大并发数量
    myQueue.maxConcurrentOperationCount = 3;
    
    //添加操作依赖
    [blockOperation1 addDependency:blockOperation2];
    [blockOperation3 addDependency:blockOperation1];
    
    //添加操作到NSOperationQueue
    [myQueue addOperation:blockOperation1];
    [myQueue addOperation:blockOperation2];
    [myQueue addOperation:blockOperation3];

打印结果:

操作依赖.png

可以看出,先执行blockOperation2,再执行blockOperation1,最后执行blockOperation3。
注意:
对于添加到queue中的操作,它的执行顺序取决于两点:

  • 首先是NSOperation是否准备好,是否准备好是由对象的依赖关系来决定。
  • 之后再根据NSOperation的相对优先级来决定,默认都是普通的优先级,可以通过setQueuePriority来设置优先级。优先级不能替代依赖关系,优先级是针对于已经准备好的NSOperation来确定执行顺序,先满足依赖关系,再根据优先级从所有准备好的操作中选择执行优先级最高的那个。
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create1----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create2----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create3----%@",[NSThread currentThread]);
    }];
NSBlockOperation *blockOperation4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"create4----%@",[NSThread currentThread]);
    }];
    [blockOperation4 setQueuePriority:NSOperationQueuePriorityVeryHigh];
//添加操作依赖
    [blockOperation1 addDependency:blockOperation2];
    [blockOperation3 addDependency:blockOperation1];
    [blockOperation4 addDependency:blockOperation2];

    [myQueue addOperation:blockOperation1];
    [myQueue addOperation:blockOperation2];
    [myQueue addOperation:blockOperation3];
    [myQueue addOperation:blockOperation4];

运行两遍打印结果:

依赖优先级.png

由结果可见,先满足依赖关系,才根据优先级从所有准备好的操作中选择优先级高的执行

//优先级:
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

自定义NSOperation

自定义NSOperation可以通过重写 main 或者start方法 来定义自己的 operations 。
使用 main方法非常简单,开发者不需要管理一些状态属性(例如 isExecuting 和 isFinished),当 main 方法返回的时候,这个 operation 就结束了。这种方式使用起来非常简单,但是灵活性相对重写 start 来说要少一些, 因为main方法执行完就认为operation结束了,所以一般可以用来执行同步任务。
重写main方法有一个注意点:

  • 需要自己创建自动释放池(如果是异步操作,无法访问主线程的自动释放池)
  • 为了能使用操作队列所提供的取消功能,你需要在长时间操作中时不时地检查 isCancelled属性
    以上是对NSOperation与NSOperationQueue的一点简单介绍,如果有哪里不对的地方欢迎拍砖交流。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容