As one of the important concurrency technologies recommended by Apple, NSOperation is also commonly used in development. This article details the two subclasses NSOperation and the use of NSOperationQueue. The author’s previous article [iOS multithreading foundation][1] has introduced in detail the simple multithreading NSThread and powerful GCD based on C language, there is a need for students can go to see. If there are three multithreading techniques, what’s the difference between them? What are the usage scenarios? I will answer them one by one at the end of this article

NSOperation is a concurrency technique recommended by Apple that provides some functionality that is not very well implemented with GCD. NSOperation is a GCD-based object-oriented package using the OC language. NSOperation is simpler to use than GCD. NSOperation is an abstract class, meaning that it should not be used directly, but rather a subclass of it. There are three ways to use its subclasses, using the two subclasses That Apple provides for us, NSInvocationOperation, NSBlockOperation, and custom subclasses that inherit from NSOperation.

NSOperation is often used in conjunction with NSOperationQueue. Any instance created using a subclass of NSOperation can be added to the NSOperationQueue operation queue. Once added to the queue, the operation is automatically executed asynchronously (note asynchronously). If the start method is used instead of adding to the queue, it is executed in the current thread.

As we know, communication between threads is mainly between the main thread and the sub-thread. Main thread to branch thread, NSOperation subclass also has corresponding methods with parameters; Threading to the main thread, such as updating the UI, also has a handy method to get the mainQueue (operations added to the mainQueue are performed on the main thread by default) : [NSOperationQueue mainQueue].

NSInvocationOperation ##1 A single NSInvocationOperation <1> directly creates an object for NSInvocationOperation, and then calls to the start method are executed directly on the main thread

Create NSOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@ (downloadImage:) object:@"Invocation"]; //2. Start method, execute directly on current thread [op start];#pragma mark - The time consuming operation called, and the time consuming operation called later is this
- (void)downloadImage:(id)obj{
  NSLog(@"% @ -- -- -- -- -- % @",[NSThread currentThread],obj);
}

Copy the code
Output [1151:50868] <NSThread: 0x7FAe624047B0 >{number = 1, name = main}-----InvocationCopy the code

<2> Add to NSOperationQueue

Create NSOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@ (downloadImage:) object:@"Invocation"]; NSOperationQueue *q = [[NSOperationQueue alloc]init]; [q addOperation:op];Copy the code
[1192:55469] <NSThread: 0x7FBe59e45C30 >{number = 3, name = (NULL)}-----InvocationCopy the code

Execute on the child thread whose number is 3 and name is null

2. Multiple NSInvocationOperation

// Queue, GCD //NSOperationQueue is essentially a concurrent queue in GCD // Operations are asynchronously executed tasks in GCD NSOperationQueue *q = [[NSOperationQueue alloc]init]; // Put multiple operations in a queuefor (int i = 0; i < 100; i++) {
    NSOperation *op = [[NSInvocationOperation alloc]initWithTarget:self 
         selector:@selector(downloadImage:) object:[NSString stringWithFormat:@"Invocation%d",i]];
    [q addOperation:op];
  }
Copy the code
**[1222:58476] <NSThread: 0x7fdc14B0cd20>{number = 7, name = (null)}-----Invocation5 **[1222:58478] <NSThread: 0x7fdc1357e5f0>{number = 9, name = (null)}-----Invocation7 **[1222:58307] <NSThread: 0x7fdc14a06ad0>{number = 3, name = (null)}-----Invocation1 **[1222:58477] <NSThread: 0x7fdc134916e0>{number = 8, name = (null)}-----Invocation6 **[1222:58481] <NSThread: 0x7fdc1357e120>{number = 12, name = (null)}-----Invocation10 **[1222:58475] <NSThread: 0x7fdc14801710>{number = 6, name = (null)}-----Invocation4 **[1222:58480] <NSThread: 0x7fdc13415630>{number = 11, name = (null)}-----Invocation9 **[1222:58306] <NSThread: 0x7FDC13512e20 >{number = 4, name = (null)}-----Invocation3 ··· ·Copy the code

The thread name and output are irregular, which is clearly a concurrent queue.

# # 2, NSBlockOperation

NSBlockOperation is used in the same way as NSInvocationOperation, but it is created in a different way. Instead of calling methods, NSBlockOperation directly uses code blocks, which is more convenient. This also makes NSBlockOperation more popular than NSInvocationOperation

NSOperationQueue *q = [[NSOperationQueue alloc]init]; NSOperationQueue *q = [NSOperationQueue mainQueue]; NSOperationQueue *q = [NSOperationQueue mainQueue]; // Put multiple operations in a queuefor (int i = 0; i < 100; i++) {
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
      NSLog(@"%@------%d",[NSThread currentThread],i); }]; // Put the Block operation in the queue [q addOperation:op]; } NSLog(@"Complete");
Copy the code
Concurrent queue output: **[1378:72440] <NSThread: 0x7F9CB2603460 >{number = 6, name = (null)}------5** **[1378:72442] <NSThread: 0x7f9cb48106a0>{number = 5, name = (null)}------7** **[1378:72441] <NSThread: 0x7f9cb242b3e0>{number = 7, name = (null)}------6** **[1378:72325] <NSThread: 0x7f9cb4851550>{number = 9, name = (null)}------2** **[1378:72320] <NSThread: 0x7f9cb492be70>{number = 4, name = (null)}------3** **[1378:72313] <NSThread: 0x7F9CB24077B0 >{number = 2, name = (null)}------1** **[1378:72276] Complete **[1378:72444] <NSThread: 0x7f9cb481cc40>{number = 11, name = (null)}------9** **[1378:72326] <NSThread: 0x7f9cb4923fe0>{number = 3, name = (null)}------0** **[1378:72440] <NSThread: 0x7f9cb2603460>{number = 6, name = (null)}------12** ... .Copy the code
**[1417:76086] <NSThread: 0x7fa452e04360>{number = 1, name = main}------0** **[1417:76086] <NSThread: 0x7fa452e04360>{number = 1, name = main}------1** **[1417:76086] <NSThread: 0x7fa452e04360>{number = 1, name = main}------2** **[1417:76086] <NSThread: 0x7fa452e04360>{number = 1, name = main}------3** **[1417:76086] <NSThread: 0x7fa452e04360>{number = 1, name = main}------4** **[1417:76086] <NSThread: 0x7fa452e04360>{number = 1, name = main}------5** **[1417:76086] <NSThread: 0x7fa452e04360>{number = 1, name = main}------6** **[1417:76086] <NSThread: 0x7fa452e04360>{number = 1, name = main}------7** ... .Copy the code

There are actually simpler ways to use NSBlockOperation

NSOperationQueue *q = [[NSOperationQueue alloc]init];
 
  for (int i = 0; i < 10; i++) {
   
    [q addOperationWithBlock:^{
      NSLog(@"%@------%d",[NSThread currentThread],i);
    }];
  }
Copy the code

3. Communication between threads

The main thread passes objects to child threads, which are already described in the previous example. The following example is going back to the main thread to update the UI.

Margin: 0.0px 0.0px 0.0px 0.0px 0.0px 0.0px; The font: 14.0 px Menlo; c NSOperationQueue *q = [[NSOperationQueue alloc]init]; [q addOperationWithBlock:^{ NSLog(@"Time-consuming operation --%@",[NSThread currentThread]);
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
      NSLog(@"Update UI -- -- -- -- -- % @",[NSThread currentThread]);
    }];
  }];
Copy the code

NSOperationQueue supports the following advanced operations: suspend queues, cancel queues, add operation dependencies, and set the maximum number of concurrent operations

<1> Maximum number of concurrent requests

@property (nonatomic,strong)NSOperationQueue *opQueue; // Override the getter method to implement lazy loading - (NSOperationQueue*)opQueue{if (_opQueue == nil) {
    _opQueue = [[NSOperationQueue alloc]init]; 
  }
  return _opQueue;
}


#pragma mark - Advanced operations: maximum number of concurrent operations/ / set the maximum number of concurrent (not the number of threads) self. OpQueue. MaxConcurrentOperationCount = 2; // Put multiple operations in a queuefor(int i = 0; i < 10; I++) {NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:3.0]; NSLog(@"%@------%d",[NSThread currentThread],i); }]; // Put the Block operation on the queue [self.opQueue addOperation:op]; }Copy the code

<2> Thread suspension

#pragma mark - Advanced operations: suspension of threads- (IBAction)pause:(UIButton *)sender {// determine the number of operations in the queue.if (self.opQueue.operationCount == 0) {
    NSLog(@"No operation on current queue");
    return; } self.opQueue.suspended = ! self.opQueue.isSuspended;if (self.opQueue.suspended) {
    NSLog(@"Pause");
   
  }else{
    NSLog(@"Continue"); }}Copy the code

<3> Cancel all operations in the queue

#pragma mark - Advanced operations: Cancel all operations in the queue- (IBAction)cancelAll:(UIButton *)sender { [self.opQueue cancelAllOperations]; [self.opQueue cancelAllOperations]; NSLog(@"Cancel all operations in the queue."); Self.opqueue.suspended = NO; // If the queue is suspended, the queue will be suspended. }Copy the code

<4> Dependencies

/* * * example * * 1. Download a novel package * 2. Unzip and delete the package * 3. Update UI */ NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"1. Download a novel zip package,%@",[NSThread currentThread]);
   
  }];
 
  NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"2. Unzip, delete compressed package,%@",[NSThread currentThread]);
   
  }];
 
  NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"% @ 3. Update the UI,",[NSThread currentThread]); }]; [op2 addDependency:op1]; [op3 addDependency:op2]; // [op1 addDependency:op3]; There must be no circular dependencies //waitUntilFinished Similar to notification of scheduling groups in GCD //NO Run the command without waiting. Come here //YES Run the command until the task is complete. Come here [self.opQueue addOperations:@[op1,op2]waitUntilFinished:YES]; // Update UI on main thread [[NSOperationQueue mainQueue] addOperation:op3]; [op3 addDependency:op2]; NSLog(@"come here");
Copy the code

There is also an NSOperationQueuePriority, a queue priority concept that is rarely used, so it will not be covered here. Those who do need it can do so on their own, or consult Documentation and API Reference.

<1>NSThread

  • Advantages: NSThread is lighter and easier to use than the other two
  • Disadvantages: need to manage thread life cycle, thread synchronization, locking, sleep, wake up, etc. Locking data for thread synchronization has some system overhead

<2>GCD GCD is a concurrent technology that didn’t appear until iOS 4.0

  • How to use: Add task to queue (serial/parallel (global)), specify method to execute task, (synchronous (blocking)/asynchronous)
  • Dispatch_get_main_queu ()
  • What NSOperation cannot do: 1. Execute once. 2. Scheduling groups (OP implementations are much more complex)

<3>NSOperation NSOperation was introduced in iOS2.0.

  • How to use: Add operations (asynchronous execution) to queues (concurrent/global)
  • Get the primary queue: [NSOperationQueue mainQueue] Primary queue, the task added to the primary queue will be executed on the main thread
  • Provides things that GCD is not good at: 1. Maximum concurrency, 2. Pause and continue, 3. Cancel all tasks. 4. Dependencies

GCD is a low-level wrapper, and we know that lower-level code generally performs better than NSOperationQueue. So the pursuit of performance, and the function is enough to consider using GCD. If the process of asynchronous operations requires more user interaction and is displayed by the UI, NSOperationQueue is a good choice. If the tasks have no dependencies between them but require higher concurrency, GCD has an advantage. Lesson from Gartner: “About 97 percent of the time, we should forget about small performance improvements. Premature optimization is the root of all evil.” Low-level GCDS are necessary only if Instruments show real performance improvements. [1]:http://www.jianshu.com/p/7267206834fb