What is NSOperation?

NSOperation is a multi-threaded solution provided by Apple. NSOperation is actually a higher level of encapsulation based on GCD, but it is more object-oriented, more readable, and more controllable than GCD, with the addition of operation dependencies.

By default, NSOperation alone can only perform operations synchronously, without the ability to initiate new threads, and can only be executed asynchronously with NSOperationQueue. At this point, it’s not hard to see that GCD and NSOperation are implemented in a very similar way, in fact, it’s more nonsense, NSOperation itself is based on GCD encapsulation, NSOperation is the equivalent of a task in GCD, NSOperationQueue is the equivalent of a queue in GCD, The essence of GCD was explained earlier in GCD for iOS Multithreaded Development (Part 1) : All a developer has to do is define the tasks they want to execute and append them to the appropriate Dispatch Queue. Thus we can also say that the essence of an NSOperation is to define the desired task (NSOperation) and append it to the appropriate NSOperationQueue.

 

2. Use of NSOperation

1. Create a task

NSOperation is an abstract base class that represents a separate cell that provides useful and thread-safe operations for subclasses such as establishment state, priority, dependency, and cancellation. However, it cannot be directly used to encapsulate tasks, but can only be encapsulated by its subclasses. Generally, we can use NSBlockOperation, NSInvocationOperation or define subclasses inherited from NSOperation to encapsulate tasks by implementing internal corresponding methods.

(1) NSInvocationOperation

- (void)invocationOperation{

    NSLog(@"start - %@",[NSThread currentThread]);
    
    // 创建NSInvocationOperation对象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(testRun) object:nil];
    
    // 调用start方法开始执行操作
    [op start];
    
    NSLog(@"end - %@",[NSThread currentThread]);
}

- (void)testRun{
    NSLog(@"invocationOperation -- %@", [NSThread currentThread]);
}Copy the code

Execution Result:

2017-07-14 13:43:59.327 beck. Wang [10248:1471363] start - <NSThread: 0x6100000614c0>{number = 1, Name = main} 2017-07-14 13:43:59.328 Beck. Wang [10248:1471363] invocationOperation -- <NSThread: 0x6100000614C0 >{number = 1, name = main} 2017-07-14 13:43:59.328 Beck. Wang [10248:1471363] end - <NSThread: 0x6100000614c0>{number = 1, name = main}Copy the code

Analysis: When NSInvocationOperation is used alone, the NSInvocationOperation performs operations in step with the main thread without starting a new thread.

(2) NSBlockOperation

- (void)blockOperation{
    
    NSLog(@"start - %@",[NSThread currentThread]);

    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"blockOperation--%@", [NSThread currentThread]);
    }];
    
    NSLog(@"end - %@",[NSThread currentThread]);
    
    [op start];
}Copy the code

Print result:

2017-07-14 13:49:25.436 Beck. Wang [10304:1476355] start - <NSThread: 0x6100000653C0 >{number = 1, name = main} 2017-07-14 13:49:25.436 beck.wang[10304:1476355] end - <NSThread: 0x6100000653C0 >{number = 1, name = main} 2017-07-14 13:49:25.436 beck.wang[10304:1476355] blockOperation--<NSThread: 0x6100000653c0>{number = 1, name = main}Copy the code

Analysis: If NSBlockOperation is used alone, NSBlockOperation also performs operations on the main thread without starting a new thread.

It is worth noting that NSBlockOperation also provides a method, addExecutionBlock:, which adds additional operations to NSBlockOperation, and these additional operations are executed concurrently in other threads.

- (void)blockOperation{ NSLog(@"start - %@",[NSThread currentThread]); NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"blockOperation--%@", [NSThread currentThread]); }]; [op addExecutionBlock:^{NSLog(@"addTask1-- %@", [NSThread currentThread]);}]; [op addExecutionBlock:^{ NSLog(@"addTask2---%@", [NSThread currentThread]); }]; [op addExecutionBlock:^{ NSLog(@"addTask3---%@", [NSThread currentThread]); }]; NSLog(@"end - %@",[NSThread currentThread]); [op start]; }Copy the code

Print result:

2017-07-14 13:57:02.009 Beck. Wang [10351:1482603] start - <NSThread: 0x60000007CDC0 >{number = 1, name = main} 2017-07-14 13:57:02.009 Beck. wang[10351:1482603] end - <NSThread: 0x60000007CDC0 >{number = 1, name = main} 2017-07-14 13:57:02.010 Beck. wang[10351:1482603] blockOperation--<NSThread: 0x60000007CDC0 >{number = 1, name = main} 2017-07-14 13:57:02.010 Beck. wang[10351:1482642] addTask1-- <NSThread: 0x618000260E00 >{number = 3, name = (null)} 2017-07-14 13:57:02.010 beck.wang[10351:1482645] addTask3-- <NSThread: 0x600000263200>{Number = 5, name = (null)} 2017-07-14 13:57:02.010 Beck. wang[10351:1482643] addTask2-- <NSThread: 0x610000264600>{number = 4, name = (null)}Copy the code

Analysis: The blockOperationWithBlock task is executed in the main thread and the addExecutionBlock task is executed in the new thread.

   

(3) Custom NSOperation subclass — override main method

    .h

@interface ZTOperation : NSOperation

@endCopy the code

    .m

@implementation ZTOperation- (void)main{NSLog(@"ZTOperation--%@",[NSThread currentThread]); } @endCopy the code

    ViewController

ZTOperation *zt = [[ZTOperation alloc] init];
[zt start];Copy the code

Print result:

2017-07-14 14:05:58.824 Beck. Wang [10389:1490955] ZTOperation--<NSThread: 0x60000007A940 >{number = 1, name = main}Copy the code

Analysis: The task is executed in the main thread without starting a new thread.

 

2. Create a queue

There are two types of NSOperationQueue: the primary queue and other queues. The other queue that contains both the function of serial and concurrent, by setting the maximum number of concurrent maxConcurrentOperationCount to achieve serial and concurrent!

(1) Main queue — Tasks are executed in the main thread

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];Copy the code

(2) Other queues — Tasks are executed in child threads

NSOperationQueue *elseQueue = [[NSOperationQueue alloc] init];Copy the code

 

NSOperation + NSOperationQueue (task appended to queue)

- (void)addOperation:(NSOperation *)op; - (void)addOperations:(NSArray<NSOperation *> *) OPS waitUntilFinished:(BOOL) Wait NS_AVAILABLE(10_6, 4_0); - (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);Copy the code

Code examples:

- (void)addOperationToQueue { NSLog(@"start - %@",[NSThread currentThread]); NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(testRun) object:nil]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"task002 -- %@", [NSThread currentThread]); }]; // Add an operation to the queue: addOperation: [queue addOperation:op1]; [queue addOperation:op2]; // Add an operation to the queue: addOperationWithBlock: [queue addOperationWithBlock:^{ NSLog(@"task003-----%@", [NSThread currentThread]); }]; NSLog(@"end - %@",[NSThread currentThread]); } - (void)testRun{ NSLog(@"task001 -- %@", [NSThread currentThread]); }Copy the code

Print result:

2017-07-14 14:39:51.669 Beck. Wang [10536:1516641] start - <NSThread: 0x610000077640>{number = 1, name = main} 2017-07-14 14:39:51.670 Beck. Wang [10536:1516641] end - <NSThread: 0x610000077640>{number = 1, name = main} 2017-07-14 14:39:51.670 Beck. wang[10536:1516686] task003-----<NSThread: 0x600000077200>{Number = 3, name = (null)} 2017-07-14 14:39:51.670 Beck. Wang [10536:1516689] Task002 -- <NSThread: 0x61800007e080>{number = 5, name = (null)} 2017-07-14 14:39:51.670 Beck. Wang [10536:1516687] Task001 -- <NSThread: 0x61000007e1c0>{number = 4, name = (null)}Copy the code

Analysis: Start a new thread and execute concurrently.

 

NSOperationQueue management

1. Queue cancellation, suspension, and recovery

– (void)cancel; NSOperation provides a method to cancel a single operation

– (void)cancelAllOperations; NSOperationQueue provides a method to cancel all operations on the queue

– (void)setSuspended:(BOOL)b; You can set the pause and resume tasks. YES indicates the pause queue, and NO indicates the resume queue

– (BOOL)isSuspended; Judge suspended state

Pausing or canceling does not cause an ongoing operation to be suspended or canceled immediately. Instead, no new operation will be performed after the current operation is completed. The difference between the two is that after suspending an operation, you can resume the operation and continue down. After canceling, all operations are cleared and no further operations can be performed.

 

2, the maximum number of concurrent maxConcurrentOperationCount

MaxConcurrentOperationCount = 1 said do not limit, the default concurrent execution;

MaxConcurrentOperationCount = 1 is the maximum number of concurrent is 1, serial execution;

MaxConcurrentOperationCount > ([count] > = 1) concurrent execution, min/count, system limitations.

Code examples:

- (void)operationQueue { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; / / set the maximum number of concurrent operation. / / the queue maxConcurrentOperationCount = 1; / / concurrent execution / / queue. MaxConcurrentOperationCount = 1; / / implementation of synchronous queue. MaxConcurrentOperationCount = 2; [queue addOperationWithBlock:^{NSLog(@"task1-----%@", [NSThread currentThread]);}]; [queue addOperationWithBlock:^{ NSLog(@"task2-----%@", [NSThread currentThread]); }]; [queue addOperationWithBlock:^{ NSLog(@"task3-----%@", [NSThread currentThread]); }]; [queue addOperationWithBlock:^{ NSLog(@"task4-----%@", [NSThread currentThread]); }]; [queue addOperationWithBlock:^{ NSLog(@"task5-----%@", [NSThread currentThread]); }]; [queue addOperationWithBlock:^{ NSLog(@"task6-----%@", [NSThread currentThread]); }]; }Copy the code

Print result:

/ / queue. MaxConcurrentOperationCount 15:28:39. = 1 2017-07-14 554 beck. Wang (10772-1557342) task2 NSThread: -- -- -- -- -- - 0x61800006D340 >{number = 4, name = (null)} 2017-07-14 15:28:39.554 Beck. wang[10772:1557358] task3-----<NSThread: 0x6080000751C0 >{number = 5, name = (null)} 2017-07-14 15:28:39.554 Beck. wang[10772:1557359] task4-----<NSThread: 0x610000071C00 >{number = 6, name = (null)} 2017-07-14 15:28:39.554 Beck. wang[10772:1557339] task5-----<NSThread: 0x60000006EA40 >{Number = 7, name = (null)} 2017-07-14 15:28:39.554 Beck. wang[10772:1557340] task1-----<NSThread: 0x608000073500>{number = 3, name = (null)} 2017-07-14 15:28:39.554 Beck. wang[10772:1557360] task6-----<NSThread: 0x610000071C80 >{number = 8, name = (null)} The number of threads is 6, Concurrent execution -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / Queue. MaxConcurrentOperationCount = 1 2017-07-14 15:27:04. 365 beck. Wang (10743-1555231) task1 NSThread: -- -- -- -- -- - 0x60800007C880 >{number = 3, name = (null)} 2017-07-14 15:27:04.365 Beck. wang[10743:1555231] task2-----<NSThread: 0x60800007C880 >{number = 3, name = (null)} 2017-07-14 15:27:04.365 Beck. wang[10743:1555231] task3-----<NSThread: 0x60800007C880 >{number = 3, name = (null)} 2017-07-14 15:27:04.365 Beck. wang[10743:1555231] task4-----<NSThread: 0x60800007C880 >{number = 3, name = (null)} 2017-07-14 15:27:04.366 Beck. wang[10743:1555231] task5-----<NSThread: 0x60800007C880 >{number = 3, name = (null)} 2017-07-14 15:27:04.366 Beck. wang[10743:1555231] task6-----<NSThread: 0x60800007C880 >{number = 3, name = (null)} The number of threads is 1, Synchronous execution -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - / / The queue. The 2017-07-14 15:18:26 maxConcurrentOperationCount = 2. 162 beck. Wang (10715-1548342) task2 NSThread: -- -- -- -- -- - 0x608000079740>{number = 4, name = (null)} 2017-07-14 15:18:26.162 Beck. wang[10715:1548344] task1-----<NSThread: 0x6100000770C0 >{number = 3, name = (null)} 2017-07-14 15:18:26.162 Beck. wang[10715:1548342] task4-----<NSThread: 0x608000079740>{number = 4, name = (null)} 2017-07-14 15:18:26.162 Beck. wang[10715:1548344] task3-----<NSThread: 0x6100000770C0 >{number = 3, name = (null)} 2017-07-14 15:18:26.162 Beck. wang[10715:1548342] task5-----<NSThread: 0x608000079740>{number = 4, name = (null)} 2017-07-14 15:18:26.163 Beck. wang[10715:1548344] task6-----<NSThread: 0x6100000770C0 >{number = 3, Name = (null)} // Analysis: The number of threads is 2 and concurrent execution is performedCopy the code

Obviously, by setting the maxConcurrentOperationCount can achieve concurrency, serial port function is more than the GCD easily!

     

3. Operation dependency

In NSOperation we can break the operation into several small tasks and operate by adding dependencies between them. This is often used! This is also the attraction of NSOperation. AddDependency does not require the same complex code as GCD.

- (void)addDependency { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ sleep(2);  NSLog(@"task1-----%@", [NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"task2-----%@", [NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"task3-----%@", [NSThread currentThread]); }]; Op1 ->op2 must be placed before [add operation queue] [op2 addDependency:op1]; Op1 addDependency:op2 [op1 addDependency:op2]; // addOperation queue [queue addOperation:op1]; [queue addOperation:op2]; [queue addOperation:op3]; }Copy the code
The 2017-07-14 15:46:02. 011 beck. Wang [10854-1571574] task3 NSThread: -- -- -- -- -- - 0x61800006D740 >{number = 3, name = (null)} 2017-07-14 15:46:04.085 Beck. wang[10854:1571596] task1-----<NSThread: 0x60000006F040 >{number = 4, name = (null)} 2017-07-14 15:46:04.085 Beck. wang[10854:1571574] task2-----<NSThread: 0x61800006d740>{number = 3, name = (null)}Copy the code

Task2 must be executed after task1 because the thread waits for 2s before executing task1.

 

4. Operation priority

NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8Copy the code

   

5. Operation monitoring

You can monitor whether an operation is completed. For example, you can download the second picture only after downloading the first one. You can set monitoring here.

- (void)addListing{ NSOperationQueue *queue=[[NSOperationQueue alloc]init]; NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{ for (int i=0; i<3; I++) {NSLog (@ "download picture 1 - % @", [NSThread currentThread]);}}]; / / operation.com surveillance operation has been completed pletionBlock = ^ {/ / continue to download pictures NSLog (@ "- download image 2 -"); }; [queue addOperation:operation]; }Copy the code

Execution Result:

[10930:1597954] 下载图 1-<NSThread: 0x61800007a340>{number = 3, name = (null)} 2017-07-14 16:21:43.834 beck.wang[10930:1597954] 下载图 1-<NSThread: 0x61800007a340>{number = 3, name = (null)} 2017-07-14 16:21:43.834 beck.wang[10930:1597954] 下载图 1-<NSThread: 0x61800007A340 >{number = 3, name = (NULL)} 2017-07-14 16:21:43.834 Beck. wang[10930:1597955Copy the code

1. Download picture 2 only after picture 1 is downloaded.

 

To leave it at the end: Multithreading isn’t just about GCD! If you haven’t used NSOperation yet, what else do you say? Practice now! Of course, they each have their own use scenarios, existence is reasonable! The three technologies of iOS multithreading are GCD, NSThread, and NSOperation. If you need to know more about GCD and NSThread, you can go back to my previous blog.