Concurrency Programming Guide

Operation Queues

NSInvocationOperation

@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

NSBlockOperation* theOp = [NSBlockOperation blockOperationWithBlock: ^{
      NSLog(@"Beginning operation.\n");
      // Do some work.
   }];
After creating a block operation object, you can add more blocks to it using the addExecutionBlock: method. If you need to execute blocks serially, you must submit them directly to the desired dispatch queue.


Defining a Custom Operation Object

For a concurrent operation, you must replace some of the existing infrastructure with your custom code.

@interface MyNonConcurrentOperation : NSOperation
@property id (strong) myData;
-(id)initWithData:(id)data;
@end
 
@implementation MyNonConcurrentOperation
- (id)initWithData:(id)data {
   if (self = [super init])
      myData = data;
   return self;
}
 
-(void)main {
   @try {
      // Do some work on myData and report the results.
   }
   @catch(...) {
      // Do not rethrow exceptions.
   }
}
@end

Responding to Cancellation Events

To support cancellation in an operation object, all you have to do is call the object’s isCancelled method periodically from your custom code and return immediately if it ever returns YES.

- (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.
   }
}

main method

Although the preceding example contains no cleanup code, your own code should be sure to free up any resources that were allocated by your custom code.

Configuring Operations for Concurrent Execution

@interface MyOperation : NSOperation {
    BOOL        executing;
    BOOL        finished;
}
- (void)completeOperation;
@end
 
@implementation MyOperation
- (id)init {
    self = [super init];
    if (self) {
        executing = NO;
        finished = NO;
    }
    return self;
}
 
- (BOOL)isConcurrent {
    return YES;
}
 
- (BOOL)isExecuting {
    return executing;
}
 
- (BOOL)isFinished {
    return finished;
}
@end

start method

- (void)start {
   // Always check for cancellation before launching the task.
   if ([self isCancelled])
   {
      // Must move the operation to the finished state if it is canceled.
      [self willChangeValueForKey:@"isFinished"];
      finished = YES;
      [self didChangeValueForKey:@"isFinished"];
      return;
   }
 
   // If the operation is not canceled, begin executing the task.
   [self willChangeValueForKey:@"isExecuting"];
   [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
   executing = YES;
   [self didChangeValueForKey:@"isExecuting"];
}

Updating an operation at completion time

- (void)main {
   @try {
 
       // Do the main work of the operation here.
 
       [self completeOperation];
   }
   @catch(...) {
      // Do not rethrow exceptions.
   }
}
 
- (void)completeOperation {
    [self willChangeValueForKey:@"isFinished"];
    [self willChangeValueForKey:@"isExecuting"];
 
    executing = NO;
    finished = YES;
 
    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

Maintaining KVO Compliance

The NSOperation class is key-value observing (KVO) compliant for the following key paths:

  • isCancelled
  • isConcurrent
  • isExecuting
  • isFinished
  • isReady
  • dependencies
  • queuePriority
  • completionBlock

When overriding the start method, the key paths you should be most concerned with are isExecuting and isFinished.

dependency

If you want to implement support for dependencies on something besides other operation objects, you can also override the isReady method and force it to return NO until your custom dependencies were satisfied.

Customizing the Execution Behavior of an Operation Object

Configuring Interoperation Dependencies

This dependency means that the current object cannot begin executing until the target object finishes executing. Dependencies are also not limited to operations in the same queue.

Changing an Operation’s Execution Priority

Priority levels are not a substitute for dependencies. Priorities determine the order in which an operation queue starts executing only those operations that are currently ready. For example, if a queue contains both a high-priority and low-priority operation and both operations are ready, the queue executes the high-priority operation first. However, if the high-priority operation is not ready but the low-priority operation is, the queue executes the low-priority operation first. If you want to prevent one operation from starting until another operation has finished, you must use dependencies (as described in Configuring Interoperation Dependencies) instead.

优先级是对ready状态的同一队列有效,如果有依赖,优先执行依赖的规则

Changing the Underlying Thread Priority

  • setThreadPriority:
    0~1.0

Setting Up a Completion Block

To set a completion block, use the setCompletionBlock: method of NSOperation. The block you pass to this method should have no arguments and no return value.

Executing Operations

Adding Operations to an Operation Queue

Operation queues work with the system to restrict the number of concurrent operations to a value that is appropriate for the available cores and system load. Therefore, creating additional queues does not mean that you can execute additional operations.

NSOperationQueue* aQueue = [[NSOperationQueue alloc] init];

加入queue的延迟时间

To add operations to a queue, you use the addOperation: method. In OS X v10.6 and later, you can add groups of operations using the addOperations:waitUntilFinished: method, or you can add block objects directly to a queue (without a corresponding operation object) using the addOperationWithBlock: method. Each of these methods queues up an operation (or operations) and notifies the queue that it should begin processing them. In most cases, operations are executed shortly after being added to a queue, but the operation queue may delay execution of queued operations for any of several reasons. Specifically, execution may be delayed if queued operations are dependent on other operations that have not yet completed. Execution may also be delayed if the operation queue itself is suspended or is already executing its maximum number of concurrent operations. The following examples show the basic syntax for adding operations to a queue.

  • setMaxConcurrentOperationCount:
    设置1的话每次执行一个任务,Although only one operation at a time may execute, the order of execution is still based on other factors, such as the readiness (准备状态) of each operation and its assigned priority. Thus, a serialized operation queue does not offer quite the same behavior as a serial dispatch queue in Grand Central Dispatch does

Executing Operations Manually

you must always start it using its start method.

An operation is not considered able to run until its isReady method returns YES. The isReady method is integrated into the dependency management system of the NSOperation class to provide the status of the operation’s dependencies. Only when its dependencies are cleared is an operation free to begin executing.

When executing an operation manually, you should always use the start method to begin execution. You use this method, instead of main or some other method, because the start method performs several safety checks before it actually runs your custom code. In particular, the default start method generates the KVO notifications that operations require to process their dependencies correctly. This method also correctly avoids executing your operation if it has already been canceled and throws an exception if your operation is not actually ready to run.
使用start开启任务执行而不是main,start会执行安全检查和生成kvo通知

if your application defines concurrent operation objects, you should also consider calling the isConcurrent method of operations prior to launching them. In cases where this method returns NO, your local code can decide whether to execute the operation synchronously in the current thread or create a separate thread first. However, implementing this kind of checking is entirely up to you.

- (BOOL)performOperation:(NSOperation*)anOp
{
   BOOL        ranIt = NO;
 
   if ([anOp isReady] && ![anOp isCancelled])
   {
      if (![anOp isConcurrent])
         [anOp start];
      else
         [NSThread detachNewThreadSelector:@selector(start)
                   toTarget:anOp withObject:nil];
      ranIt = YES;
   }
   else if ([anOp isCancelled])
   {
      // If it was canceled before it was started,
      //  move the operation to the finished state.
      [self willChangeValueForKey:@"isFinished"];
      [self willChangeValueForKey:@"isExecuting"];
      executing = NO;
      finished = YES;
      [self didChangeValueForKey:@"isExecuting"];
      [self didChangeValueForKey:@"isFinished"];
 
      // Set ranIt to YES to prevent the operation from
      // being passed to this method again in the future.
      ranIt = YES;
   }
   return ranIt;
}

Canceling Operations

Once added to an operation queue, an operation object is effectively owned by the queue and cannot be removed. The only way to dequeue an operation is to cancel it. You can cancel a single individual operation object by calling its cancel method or you can cancel all of the operation objects in a queue by calling the cancelAllOperations method of the queue object.
You should cancel operations only when you are sure you no longer need them. Issuing a cancel command puts the operation object into the “canceled” state, which prevents it from ever being run. Because a canceled operation is still considered to be “finished”, objects that are dependent on it receive the appropriate KVO notifications to clear that dependency. Thus, it is more common to cancel all queued operations in response to some significant event, like the application quitting or the user specifically requesting the cancellation, rather than cancel operations selectively.

cancel也会促发kvo,cancel、
状态等同于finish,会让依赖清除

Waiting for Operations to Finish

  • waitUntilFinished
  • waitUntilAllOperationsAreFinished

Suspending and Resuming Queues

  • setSuspended:
    只对队列中没有执行的operation有效

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

推荐阅读更多精彩内容

  • 你不一定要点蓝字关注我的 “你站在桥上看风景,看风景的人在楼上看你。 明月装饰了你的窗子,你装饰了别人的梦。” —...
    魁歌阅读 2,763评论 0 1
  • 2017年3月21日是第17个世界睡眠日,主题是“健康睡眠,远离慢病”。 进入21世纪,人们的健康意识空前提高,“...
    NANA0阅读 156评论 0 0
  • 放下笔(嗯,键盘),休息一下。 让灵魂,等等疲惫的身体。 早安午安和晚安。
    闲云囧人阅读 282评论 0 1
  • 必看的两本书 《外部空间设计》(日)卢原义信著,尹培桐译。 《建筑形式的逻辑概念》,(德)托马斯·史密特 著,肖毅...
    一榀居阅读 527评论 0 2
  • 最近运营团队在公众号上的运营上遇到点困难,尝试着用产品的思维去思考整个运营,如有不对,请指出。 部分内容,借鉴其他...
    加盐大树阅读 470评论 0 5