iOS多线程-NSOperation

NSOperation是对GCD的高级封装。相对于GCD,使用NSOperation更加符合面对对象的编程习惯,更重要的是,NSOperation提供了更多的特性方便开发者监控和管理要并发执行的任务:

  1. 可以简便地设置任务之间的依赖关系
  2. 可以对取消正在执行的任务
  3. 可以只用KVO的方式来监控任务的当前状态
  4. 可以针对任务设置优先级
  5. 可以为任务设置一个完成后执行的competitionBlock

创建NSOperation

NSOperation有两个具体的子类,NSInvocationOperation和NSBlockOperation,创建方法如下(官方Demo):

@implementation MyCustomClass
- (NSOperation*)taskWithData:(id)data {
    NSInvocationOperation* theOp = [[NSInvocationOperation alloc] initWithTarget:self
                    selector:@selector(myTaskMethod:) object:data];
 
   return theOp;
}
 
// This is the method that does the actual work of the task.
- (void)myTaskMethod:(id)data {
    // Perform the task.
}
@end
NSBlockOperation* theOp = [NSBlockOperation blockOperationWithBlock: ^{
      NSLog(@"Beginning operation.\n");
      // Do some work.
   }];

NSOperation的同步和异步执行

NSOperation对象调用start方法执行,默认是在调用的线程同步执行的。当一个线程调用start方法时,operation会立即在该线程中执行。当operation结束后线程才会继续执行之后的代码。

对一个异步的operation来说,当调用其start方法时,该operation可能会开启一个新的线程执行,调用一个异步方法或者将block提交到一个dispatch_queue,总之调用的线程会继续执行余下的代码而不会等待operation完成。

NSOperationQueue

大多数情况下并不需要创建一个异步的operation,这需要开发者做很多额外的工作。更普遍的做法是将NSOperation放进NSOperationQueue中去执行。当你将一个operation加进operation queue中,那么无论该operation的asynchronous 属性是什么,调用者都会在一个另外一个的线程中去执行start方法。所以当使用NSOperationQueue来管理执行的话,完全没有必要创建异步的operation。

当operation添加进operation queue后,不需要调用任何方法,operation queue 中的operation就会开始执行。

设置依赖

用法简单,直接上一段代码:

- (void)opDependency{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSOperation *calculateOp1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start do some calulation 1");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"end cal 1, do some update");
    }];
    [queue addOperation:calculateOp1];
    
    NSOperation *calculateOp2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start do some calulation 2");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"end cal 2, do some update");
    }];
    [queue addOperation:calculateOp2];
    
    NSOperation *doAfterPreOpDone = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"do something which depend on op1 and op2");
    }];
    [doAfterPreOpDone addDependency:calculateOp1];
    [doAfterPreOpDone addDependency:calculateOp2];
    
    [queue addOperation:doAfterPreOpDone];
}

运行结果为:

2017-04-20 15:53:06.062 ConcurrentProgram[98523:43310892] start do some calulation 1
2017-04-20 15:53:06.062 ConcurrentProgram[98523:43310890] start do some calulation 2
2017-04-20 15:53:08.134 ConcurrentProgram[98523:43310890] end cal 2, do some update
2017-04-20 15:53:09.137 ConcurrentProgram[98523:43310892] end cal 1, do some update
2017-04-20 15:53:09.137 ConcurrentProgram[98523:43310890] do something which depend on op1 and op2

取消NSOperation

将上面的代码稍微改动一下,测试一下NSOperation的cancel功能。

- (void)opDependency{
    __block long i = 0;
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSOperation *calculateOp1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start do some calulation 1");
        while (true) {
            i++;
        }
    }];
    [queue addOperation:calculateOp1];
    
    NSOperation *calculateOp2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start do some calulation 2");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"end cal 2, do some update");
        [calculateOp1 cancel];
    }];
    [queue addOperation:calculateOp2];
    
    NSOperation *doAfterPreOpDone = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"do something which depend on op1 and op2");
        NSLog(@"i is %ld",i);
    }];
    [doAfterPreOpDone addDependency:calculateOp1];
    [doAfterPreOpDone addDependency:calculateOp2];
    
    [queue addOperation:doAfterPreOpDone];
}

在calculateOp1中设置了一个无限循环的block,然后我们希望在calculateOp2中去取消掉calculateOp1。预期结果是在calculateOp2中的block执行完以后会立即执行doAfterPreOpDone中的任务。(因为另一个依赖被取消了)

结果如下:

2017-04-20 16:38:01.658 ConcurrentProgram[542:43544549] start do some calulation 1
2017-04-20 16:38:01.658 ConcurrentProgram[542:43544548] start do some calulation 2
2017-04-20 16:38:03.730 ConcurrentProgram[542:43544548] end cal 2, do some update

发现calculateOp1并没有取消而doAfterPreOpDone一直不会执行。

去查阅官方的API文档,发现有这样的一段话:

Canceling an operation does not immediately force it to stop what it is doing. Although respecting the value in the cancelled property is expected of all operations, your code must explicitly check the value in this property and abort as needed. The default implementation of NSOperation includes checks for cancellation. For example, if you cancel an operation before its start method is called, the start method exits without starting the task.

也就是说调用cancel方法并不会立即停止operation中的动作,只是将cancelled属性设置为YES,真正的取消动作是需要开发者自己实现的。一般来说,要继承NSOperation类,在start方法中不停地检查isCancel属性。在默认的NSOeration的实现中也包括了cancel属性的检查,如果你在operation的start方法之前cancel了,那么该operation就不会执行了。

官方提供了一个示例(将main方法改为start方法效果一样)

- (void)main {
   @try {
      BOOL isDone = NO;
 
      while (![self isCancelled] && !isDone) {
          // Do some work and set isDone to YES when finished
      }
   }
   @catch(...) {
      // Do not rethrow exceptions.
   }
}   

那么之前的代码相当于:

- (void)main {
    @try {
        BOOL isDone = NO;
        
        while (![self isCancelled] && !isDone) {
            NSLog(@"start do some calulation 1");
            while (true) {
                i++;
            }
        }
    }
    @catch(...) {
        // Do not rethrow exceptions.
    }
}

检查cancel属性的代码在打印之前,在打印之后取消该block就没有效果了。

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

推荐阅读更多精彩内容