Good articles to my personal technology blog: https://cainluo.github.io/15019907895337.html


In the previous chapter, we touched on some basic concepts and some simple uses of NSOperation, but in the example, we found that NSOperation alone would only be executed in the main thread. Although NSBlockOperation can add additional tasks to execute in child threads, this is not enough, so this time we are going to use NSOperation and NSOperationQueue together to complete.

If you missed the previous article, check out playing iOS Development: NSOperation Development in iOS (1).

Reprint statement: if you need to reprint this article, please contact the author, and indicate the source, and can not modify this article without authorization.


NSOperationQueue

NSOperationQueue is a little bit different from GCD queues, in NSOperationQueue, there are primary queues and other queues, whereas GCD has parallel queues, serial queues.

Other queues in NSOperationQueue have parallel and serial functions, so let’s see how they work:

Create primary queue:

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
Copy the code
  • Any tasks added to the main queue are put into the main thread for execution.

Create additional queues:

NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init];
Copy the code
  • Tasks added to other queues are automatically put into sub-threads for execution, both in parallel and serial mode.

Add the task to the NSOperationQueue

As we mentioned earlier, NSOperation needs to work with NSOperationQueue to implement multi-threading, so there are two ways to do this.

The first way

Use the following method to add tasks to the queue:

- (void)addOperation:(NSOperation *)op;
Copy the code
- (void)addMissionToOperationQueue {
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                                      selector:@selector(runInvocationOperation)
                                                                                        object:nil];
    
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        
        for (NSInteger i = 0; i < 2; i++) {
            
            NSLog(@"blockOperation executes task %zd, the current thread is: %@", i, [NSThreadcurrentThread]); }}]; [queue addOperation:invocationOperation]; [queue addOperation:blockOperation]; } - (void)runInvocationOperation {
    
    for (NSInteger i = 0; i < 3; i++) {
        
        NSLog(@"invocationOperation executes task %zd, current thread: %@", i, [NSThreadcurrentThread]); }}Copy the code
2017-08-06 12:06:53.950 NSOperationQueue-Example[2325:157835] invocationOperation Executes the code0<NSThread:0x60000006c540>{number = 3, name = (null)}
2017-08-06 12:06:53.950 NSOperationQueue-Example[2325:157825] blockOperation Specifies the operation number0<NSThread:0x608000074180>{number = 4, name = (null)}
2017-08-06 12:06:53.955 NSOperationQueue-Example[2325:157835] invocationOperation Executes the code1<NSThread:0x60000006c540>{number = 3, name = (null)}
2017-08-06 12:06:53.989 NSOperationQueue-Example[2325:157825] blockOperation Specifies the operation number1<NSThread:0x608000074180>{number = 4, name = (null)}
2017-08-06 12:06:53.990 NSOperationQueue-Example[2325:157835] invocationOperation Executes the code2<NSThread:0x60000006c540>{number = 3, name = (null)}
Copy the code
  • As anyone who read the last article knows, use it directlyNSInvocationOperationandNSBlockOperation(does not use the method of adding extra tasks) will only be executed in the main thread, but if it worksNSOperationQueueTo use, you can automatically start the child thread, and is synchronous execution.

The second way

Use the following method to add tasks to the queue:

- (void)addOperationWithBlock:(void(^) (void))block NS_AVAILABLE(10_6, 4_0);
Copy the code
- (void)addMissionToPerationQueueBlock {
    
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
    
    [operationQueue addOperationWithBlock:^{
        
        for (NSInteger i = 0; i < 3; i++) {
            
            NSLog(@" Execute task %zd, current thread: %@", i, [NSThreadcurrentThread]); }}]; }Copy the code
2017-08-06 12:13:16.518 NSOperationQueue-Example[2382:165154] the first0<NSThread:0x60000007c140>{number = 3, name = (null)}
2017-08-06 12:13:16.519 NSOperationQueue-Example[2382:165154] the first1<NSThread:0x60000007c140>{number = 3, name = (null)}
2017-08-06 12:13:16.519 NSOperationQueue-Example[2382:165154] the first2<NSThread:0x60000007c140>{number = 3, name = (null)}
Copy the code
  • As you can see from the results, this method is much cleaner code and can also start new threads and execute tasks synchronously.

Note: It is also important to note that we do not need to invoke the task manually when adding it to the queue- (void)star;Method, which the queue will automatically call.


Freely switch serial/parallel queues

What if we want a serial task? At this point we only need to change one thing:

@property NSInteger maxConcurrentOperationCount;
Copy the code
- (void)changeSyncOrAsyncQueue {
    
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
    
    // If serial queue is required, set to 1, otherwise not, default parallel queue
    operationQueue.maxConcurrentOperationCount = 1;
    
    [operationQueue addOperationWithBlock:^{
        
        NSLog("Execute first task, current thread: %@"[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:0.1];
    }];
    
    [operationQueue addOperationWithBlock:^{
        
        NSLog(@" Execute second task, current thread: %@"[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:0.1];
    }];

    [operationQueue addOperationWithBlock:^{
        
        NSLog(@" Execute third task, current thread: %@"[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:0.1];
    }];
    
    [operationQueue addOperationWithBlock:^{
        
        NSLog(@" Perform the fourth task, current thread: %@"[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:0.1];
    }];

    [operationQueue addOperationWithBlock:^{
        
        NSLog("Execute the fifth task, the current thread is: %@"[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:0.1];
    }];
}
Copy the code
2017-08-06 12:24:24.786 NSOperationQueue-Example[2571:179165] execute the first task. The current thread is <NSThread:0x6000000771c0>{number = 3, name = (null)}
2017-08-06 12:24:24.887 NSOperationQueue-Example[2571:179162] perform the second task, the current thread is: <NSThread:0x600000076840>{number = 4, name = (null)}
2017-08-06 12:24:24.992 NSOperationQueue-Example[2571:179165] perform the third task, the current thread is: <NSThread:0x6000000771c0>{number = 3, name = (null)}
2017-08-06 12:24:25.096 NSOperationQueue-Example[2571:179162<NSThread: <NSThread:0x600000076840>{number = 4, name = (null)}
2017-08-06 12:24:25.200 NSOperationQueue-Example[2571:179162] perform the fifth task. The current thread is <NSThread:0x600000076840>{number = 4, name = (null)}
Copy the code
  • As can be seen from the above results, when we set the maximum concurrency to1You can tell it isThe serial execution(Parallel execution), and the number of open threads is also determined by the system.

Dependencies between tasks

The most interesting aspect of NSOperation and NSOperationQueue is this dependency, which allows a task to be dependent on another task. Dependent tasks are executed only when the dependent task is completed.

- (void)addDependency {
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *blockOperationOne = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@" Execute first task, current thread: %@"[NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOperationTwo = [NSBlockOperation blockOperationWithBlock:^{

        NSLog("Execute second task, current thread: %@"[NSThread currentThread]);
    }];
    
    [blockOperationTwo addDependency:blockOperationOne];
    
    [queue addOperation:blockOperationOne];
    [queue addOperation:blockOperationTwo];
}
Copy the code
2017-08-06 13:34:38.095 NSOperationQueue-Example[2962:210009<NSThread:0x600000075b80>{number = 3, name = (null)}
2017-08-06 13:34:38.110 NSOperationQueue-Example[2962:210009<NSThread:0x600000075b80>{number = 3, name = (null)}
Copy the code
  • As a result, the second task is performed after the first, no matter how many times or how many times it is delayed.
  • Moreover, even if the second task is enqueued first, the first task is executed before the second.

Other methods of NSOperation

  • You can cancelNSOperationA single operation of.
- (void)cancel;
Copy the code
  • NSOperationQueueProvides a method to cancel all operations in a queue
- (void)cancelAllOperations;
Copy the code
  • Set up theNSOperationQueuethesuspendedProperty to pause and resume the operation of a task,YESStands for pause queue,NORepresents the recovery queue.
@property (getter=isSuspended) BOOL suspended;
Copy the code
  • NSOperationQueuetheisSuspendedYou can determine whether a task operation is paused or resumed.

Note: The pause operation does not mean that the current operation is cancelled immediately, but that the current task is not executed after the completion of the task. A pause is a temporary pause that can be resumed later, while a cancel means that you cancel all actions so that you cannot continue with the rest of the actions.


conclusion

Okay, so that’s pretty much all we know about NSOperation, but if you want to go into more detail, you can go to the official NSOperation documentation, the official NSOperationQueue documentation.


The project address

The address of the project: https://github.com/CainRun/iOS-Project-Example/tree/master/NSOperationQueue-Example


The last