Basic concepts of iOS multithreading

NSThread for iOS Multithreading

NSOperation for iOS Multithreading

GCD for iOS Multithreading

NSOperation profile

NSOperation role

Using NSOperation and NSOperationQueue together is also a multithreaded solution provided by Apple. NSOperation is based on a higher level of encapsulation of the GCD and is fully object oriented. But it is easier to use than GCD and the code is more readable.

NSOperation versus GCD

  1. GCD is a pure C API, while operation queue isOCThe object.
  2. In GCD, tasks are represented by blocks, which are lightweight data structures; The operation in the operation queue is a heavier OC object.

Benefits of using NSOperation

  1. Can be conveniently calledcancelMethod to cancel an operation, whereas tasks in GCD cannot be canceled.
  2. Can specify the dependencies between operations, convenient control of the execution sequence.
  3. Can be achieved byKVOProvides fine control over the object of the operation (such as listening for whether the current operation has been cancelled or completed).
  4. You can easily specify the priority of operations.
  5. You can customize subclasses of NSOperation so you can reuse operations.

Operation/operation queue

NSOperation operation:

Encapsulate executed tasks. Place tasks in blocks on the GCD.

The NSOperation class is an abstract class that is not used directly, but either subclasses (NSInvocationOperation or NSBlockOperation) or custom NSOperations that encapsulate operations by implementing internal methods. Perform actual tasks.

NSOperationQueue Indicates the operation queue.

A queue used to hold and control operations. This is different from the SCHEDULING queue FIFO (first in, first out) principle in GCD. The operation queue performs queued operations based on their priority and readiness.

Implementation of multithreaded use steps

NSOperation needs to work with NSOperationQueue to implement multithreading:

  1. Create operation: Encapsulate the operation to be performed into an NSOperation object.
  2. Create queue: Create an NSOperationQueue object.
  3. Queue an operation: Adds an NSOperation object to an NSOperationQueue object.

NSOperation Basic use

Encapsulate (create) operations

Using the actions alone, you can perform the actions yourself by calling the start method. NSOperation is an abstract class and cannot be used to encapsulate operations. Only its subclasses are used to encapsulate operations. There are three ways to encapsulate operations.

  1. Use the subclass NSInvocationOperation
  2. Use the subclass NSBlockOperation
  3. Custom subclasses of NSOperation, implementing internal methods to encapsulate operations.

NSInvocationOperation

  • Used aloneNSInvocationOperationExecutes synchronously on the current thread, with no new lines opened.
/// Use NSInvocationOperation alone to execute synchronously on the current thread, with no new line enabled
- (void)invocationOperation {
    //01 Encapsulate the operation object
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
    NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
    NSInvocationOperation *op4 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
    //02 Perform operations
    [op1 start];
    [op2 start];
    [op3 start];
    [op4 start];
}
- (void)download {
  [NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
  NSLog(@ "% @".NSThread.currentThread);
}
Copy the code

NSBlockOperation

  • Used alone to makeNSBlockOperationPerforms an operation that is performed on the current thread without starting a new thread.
  • However,NSBlockOperationA method is also providedaddExecutionBlock:, which allows you to add additional tasks.
  • These tasks (includingblockOperationWithBlock:Can be executed simultaneously (concurrently) in different threads.
  • If you add a lot of tasks,blockOperationWithBlock:At the system’s discretion, a task may also be executed in another (non-current) thread.
  • An operation is considered to be complete only when all tasks of the operation are completed.
- (void)blockOperation {
    //01 Encapsulate the operation object
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2];// Simulate time-consuming operations
        NSLog(1 - % @ "@".NSThread.currentThread);
    }];

    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2];// Simulate time-consuming operations
        NSLog(2 - % @ "@".NSThread.currentThread);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2];// Simulate time-consuming operations
        NSLog(@ 3.0 - % @ "".NSThread.currentThread);
    }];
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2];// Simulate time-consuming operations
        NSLog(4 - % @ "@".NSThread.currentThread);
    }];
  
    // Add additional tasks
    // When the number of tasks in an operation is greater than 1, the subthread is enabled to execute tasks with the current thread
    [op3 addExecutionBlock:^{
        [NSThread sleepForTimeInterval:2];// Simulate time-consuming operations
        NSLog(@ 3.1 - % @ "".NSThread.currentThread);
    }];
    
    [op3 addExecutionBlock:^{
        [NSThread sleepForTimeInterval:2];// Simulate time-consuming operations
        NSLog(@ 3.2 - % @ "".NSThread.currentThread);
    }];
    //02 Perform operations
    [op1 start];
    [op2 start];
    [op3 start];
    [op4 start];
}
Copy the code
- (void)blockOperationAddExecutionBlock {
    // 1. Create NSBlockOperation objects
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
            NSLog(@"1-%i---%@",i, [NSThreadcurrentThread]); }}];// 2. Add additional tasks
    [op addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
            NSLog(@"2-%i---%@",i, NSThread.currentThread); }}]; [op addExecutionBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
            NSLog(@"3-%i---%@",i, NSThread.currentThread); }}]; [op addExecutionBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
            NSLog(@"4-%i---%@",i,NSThread.currentThread); }}]; [op addExecutionBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
            NSLog(@"5-%i---%@",i, NSThread.currentThread); }}]; [op addExecutionBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
            NSLog(@"6-%i---%@",i, NSThread.currentThread); }}]; [op addExecutionBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
            NSLog(@"7-%i---%@",i, NSThread.currentThread); }}]; [op addExecutionBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
            NSLog(@"8-%i---%@",i, NSThread.currentThread); }}];//03 Perform operations
    [op start];
}
Copy the code

NSOperation custom

If subclasses do not meet the requirements, you can customize NSOperation. Then override the – (void)main method to implement the desired task inside. When main returns from execution, the operation is considered complete.

rewrite- (void)mainNote:

  • Create your own automatic release pool (because the automatic release pool for the main thread cannot be accessed if the operation is asynchronous)

  • Often responds to cancellation by checking whether the operation isCancelled by the – (BOOL)isCancelled method

Advantages of custom NSOperation: Facilitates code encapsulation and reuse

#/ / ZYOperation. M file
#import "ZYOperation.h"

@implementation ZYOperation

- (void)main {
    
    if (self.isCancelled) return;
    for (int i = 0; i < 10000; i++) {
        NSLog(@"%i - %@",i,NSThread.currentThread);
    }
    
    if (self.isCancelled) return;
    for (int i = 10000; i < 20000; i++) {
        NSLog(@"%i - %@",i,NSThread.currentThread);
    }
    
    if (self.isCancelled) return;
    for (int i = 20000; i < 30000; i++) {
        NSLog(@" %i - %@",i,NSThread.currentThread);
    }
    
    if (self.isCancelled) return;
    for (int i = 30000; i < 40000; i++) {
        NSLog(@"%i - %@",i,NSThread.currentThread); }}@end
Copy the code

In the case of using a custom subclass inherited from NSOperation alone, the operation is performed on the current thread and no new thread is started.

- (void)customOperation {
    ZYOperation *op = [[ZYOperation alloc] init];
    [op start];
}
Copy the code

NSOperation Common properties and methods

Cancel operation method

- (void)cancel;
Copy the code

You can cancel the operation. This method does not force the operation code to stop, but rather marks the isCancelled status to YES. This method is invalid if the operation has already completed execution. You can only cancel operations that process the waiting state in the queue. The current operation is indivisible and must be completed.

Get operational status

  • @property(readonly, getter=isCancelled) BOOL cancelled;

A Boolean value indicating whether the operation has been canceled. The default value is NO. The value of this property is set to YES by calling the object’s cancel method. The action object is responsible for periodically calling this method and stopping the action when YES is returned.

The value of this property should always be checked before completing the action task, usually at the beginning of the custom main method and before each time-consuming operation. An operation can be canceled before it starts or at any time during execution. Therefore, checking the value of the property at the beginning of the main method (and periodically throughout the method) can exit as soon as the operation is cancelled.

  • @property(readonly, getter=isExecuting) BOOL executing;

A Boolean value indicating whether the operation is currently executing. This property has a value of YES if the operation is currently performing its main task; Otherwise, the value of this property is NO.

When implementing concurrent manipulation objects, the implementation of this property must be overridden to return the execution status of the operation. When implementing concurrent manipulation objects, the implementation of this property must be overridden so that the execution status of the operation can be returned. In a custom implementation, KVO notifications must be generated for the key path whenever the execution state of the action object changes.

There is no need to reimplement this property for non-concurrent operations.

  • @property(readonly, getter=isFinished) BOOL finished;

A Boolean value indicating whether the operation has completed status. This property has a value of YES if the operation has completed the task; If the task is executing or has not been started, the value of this property is NO.

When implementing concurrent manipulation objects, the implementation of this property must be overridden so that the completion state of the operation can be returned. In a custom implementation, a KVO notification must be generated for the key path every time the completion state of the action object changes. For more information on manually generating KVO notifications, see the Key-Value Observation Programming Guide. isFinished

There is no need to reimplement this property for non-concurrent operations.

  • @property(readonly, getter=isReady) BOOL ready;

A Boolean value indicating whether an operation is in a ready state (ready to perform). The ready state of operations depends on their dependence on other operations, and may also depend on custom conditions that you define. This NSOperation class manages the dependencies of other operations and reports on the readiness to receive based on those dependencies.

If you want to use custom conditions to define the ready state of an action object, reimplement this property and return a value that accurately reflects the ready state of the recipient. If you do, your custom implementation must get the default property value super from it and incorporate the ready value into the new value for that property. In a custom implementation, KVO notifications must be generated for the critical path every time the ready state of the action object changes.

dependencies

- (void)addDependency:(NSOperation *)op;

Adds a dependency that makes the current operation dependent on the completion of the operation OP. Adding a dependency has no real effect if the current operation is already executing a task. This method may change the isReady and Dependencies properties of the current operation.

Creating any circular dependencies between a set of operations results in deadlocks between the operations.

- (void)removeDependency:(NSOperation *)op;

Remove the dependence of the current operation on the op. This method may change the isReady and Dependencies properties of the current operation.

@property(readonly, copy) NSArray<NSOperation *> *dependencies;

Array of all operation objects that the current operation depends on. This property contains an array of NSOperation objects. To add objects to this array, use the addDependency: method.

An action object must be executed after all of its dependent operations have been executed. The operation will not be removed from this dependency array after the operation is completed. You can use this array to keep track of all related operations, including those that have completed execution. The only way to remove action from this list is to use the removeDependency: method.

NSOperationQueue basic use

There are two types of NSOperationQueue: main queue and custom queue. The custom queue includes both serial and concurrent functions.

The home side column

With mainQueue, any tasks placed in the mainQueue will be executed on the main thread.

  • Note: Operational use is not includedaddExecutionBlock:Additional operations are added that may be performed on other threads.
// Obtain the main queue
NSOperationQueue *queue = [NSOperationQueue mainQueue];
Copy the code

The master queue

You just alloc init it. Non-primary queues can be both concurrent and serial (the default is concurrent queues), with the maximum concurrency attribute set to control whether tasks are executed concurrently or serially.

// Customize the queue creation method
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
Copy the code

Add the action to the queue

NSOperation needs to work with NSOperationQueue to implement multi-threading, to queue the created operations. An action object only in one operation in the queue, if the operation has been another queue, then this method will throw NSInvalidArgumentException anomalies. If the current operation is executed or completed, this method will throw NSInvalidArgumentException anomalies.

  • useaddOperation:Adding an operation to the operation queue can start a new thread for concurrent execution.
//01 Create a queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//02 Encapsulation operation
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"1-%i---%@",i, NSThread.currentThread); }}];NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"2-%i---%@",i, NSThread.currentThread); }}];NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"3-%i---%@",i, NSThread.currentThread); }}];NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"4-%i---%@",i, NSThread.currentThread); }}];// Create the operation first and then queue it
//03 Add action to queue
[queue addOperation:op1]; // Call [op1 start] internally;
[queue addOperation:op2]; // Call [op2 start] internally;
[queue addOperation:op3]; // Call [op3 start] internally;
[queue addOperation:op4]; // Call [op4 start] internally;
Copy the code
  • addOperationWithBlock:

This method starts by wrapping a single Block in an action object, adding the action to the action queue. Failed to obtain the operation object information. The ability to start new threads for concurrent execution.

- (void)addOperationWithBlock {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  
  // Directly queue the block containing the operation
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1-%i---%@",i, NSThread.currentThread); }}]; [queue addOperationWithBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2-%i---%@",i, NSThread.currentThread); }}]; [queue addOperationWithBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3-%i---%@",i, NSThread.currentThread); }}]; [queue addOperationWithBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"4-%i---%@",i, NSThread.currentThread); }}]; }Copy the code
  • - (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait

A set of operations is added to the operation queue. If wait is YES, the current thread will be blocked. The added set of operations will be executed in order (the tasks within the operation are concurrently) until all the specified operations are completed.

//01 Create a queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//02 Encapsulation operation
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"1-%i---%@",i, NSThread.currentThread); }}];NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"2-%i---%@",i, NSThread.currentThread); }}];NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"3-%i---%@",i, NSThread.currentThread); }}];NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"4-%i---%@",i, NSThread.currentThread); }}];// Create the operation first and then queue it
//03 Add action to queue
[queue addOperation:op1]; // Call [op1 start] internally;
// The following operations must wait for op2, OP3 (in order) to complete.
[queue addOperation:@[op2, op3] waitUntilFinished:YES]; 
[queue addOperation:op4]; // Call [op4 start] internally;
Copy the code
  • 六四事件addBarrierBlock:** Prevents any subsequent operations from being performed until this operation is complete as is the dispatch_barrier_async function.
- (void)addOperationWithBlock {
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1-%i---%@",i, NSThread.currentThread); }}]; [queue addOperationWithBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2-%i---%@",i, NSThread.currentThread); }}];// This function is similar to the dispatch_barrier_async function.
    [queue addBarrierBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"%i+++++%@",i, NSThread.currentThread); }}]; [queue addOperationWithBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3-%i---%@",i, NSThread.currentThread); }}]; [queue addOperationWithBlock:^{for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"4-%i---%@",i, NSThread.currentThread); }}]; }Copy the code

Control serial/concurrent execution

  • NSOperationQueue created custom queue by setting the maxConcurrentOperationCount change serial and concurrent function
  • MaxConcurrentOperationCount: maximum number of concurrent operation. Used to control how many operations can participate in concurrent execution in a queue.
  • This property controls not the number of concurrent threads, but the maximum number of simultaneous operands that can be executed concurrently.
  • An operation does not have to run on only one thread.(If an operation has multiple tasks, they can be executed concurrently on different threads.)
  • The default value of this attribute is -1, indicating that concurrent execution is not restricted.
  • When set to 1 and there is only one task per operation, the queue is a serial queue. It can only be executed sequentially. After one operation is complete, the next operation starts
  • When the value is set to 1, if an operation has multiple tasks, the tasks in the operation are concurrently executed

Operate dependencies and listeners

NSOperation, NSOperationQueue it adds dependencies between operations. By relying on operations, you can easily control the execution sequence of operations.

  • – (void)addDependency:(NSOperation *)op; Adds a dependency that makes the current operation dependent on the completion of the operation OP.

  • – (void)removeDependency:(NSOperation *)op; Remove the dependence of the current operation on the op. `

  • @property (readonly, copy) NSArray<NSOperation *> *dependencies;

    An array of all operation objects that completed execution before the current operation began.

  • @property(copy) void (^completionBlock)(void); The block to be executed after the main task of the operation is completed.

//01 Create a queue
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    NSOperationQueue *queue2 = [[NSOperationQueue alloc]init];
    
    //02 Encapsulate the operation object
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(1 - % @ "@".NSThread.currentThread);
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(2 - % @ "@".NSThread.currentThread);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(3 - % @ "@".NSThread.currentThread);
    }];
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(4 - % @ "@".NSThread.currentThread);
    }];
    
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(5 - % @ "@".NSThread.currentThread);
    }];
    
    // The listening task is completed
    op4.completionBlock = ^{
        NSLog(@ "op4 complete % @".NSThread.currentThread);
    };
    
    //03 Set operation dependency :4->3->2->1->5
    //⚠️ cannot set a circular dependency. As a result, neither task will be executed, and if there are any tasks that depend on either task, neither task will be executed
    [op5 addDependency:op1];
    [op1 addDependency:op2];
    //[op2 addDependency:op1];
    [op2 addDependency:op3];
    [op3 addDependency:op4];
    
    //04 Add action to queue
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue2 addOperation:op4];
    [queue2 addOperation:op5];
Copy the code

Operation priority

NSOperation provides the queuePriority (priority) attribute. The queuePriority attribute applies to the operations in the same operation queue, but not to the operations in different operation queues. By default, all the operation of the newly created object is NSOperationQueuePriorityNormal priority. However, we can change the priority of the current operation in the same queue by using the setQueuePriority: method.

// The value of priority
typedef NS_ENUM(NSInteger.NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = - 8 -L,
    NSOperationQueuePriorityLow = 4 -L,
    NSOperationQueuePriorityNormal = 0.NSOperationQueuePriorityHigh = 4.NSOperationQueuePriorityVeryHigh = 8
};
Copy the code
  • When all the dependencies of an operation have been completed (or none), the operation object usually enters the ready state, waiting to be executed.
  • The start order (not the end order) of operations in the ready state is determined by their relative priority (which is a property of the operation object itself).
  • queuePriorityProperty that determines the start order of execution between all operations in the ready state. Also, priorities do not replace dependencies.
  • If a queue contains both high – and low-priority operations and both are ready, the queue performs the high – priority operation first.
  • In a queue, operations that are not ready have a higher priority than those that are ready. Ready operations are also prioritized.
  • Priorities do not replace dependencies. If you want to control the start order between operations, you must use dependencies.

Communication between threads

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        
        NSURL *url = [NSURL URLWithString:@"http://pic65.nipic.com/file/20150503/19114660_160640830000_2.jpg"];
        NSData *imagweData = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:imagweData];
        
        [NSOperationQueue.mainQueue addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    //KVO
    //[op addObserver:self forKeyPath:@"isCancelled" options:NSKeyValueObservingOptionNew context:nil];
    [queue addOperation:op];
}

//- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
/ /}
Copy the code

NSOperationQueue Other usages

/ / ZYOperation. M file
#import "ZYOperation.h"

@implementation ZYOperation

- (void)main {
    
    if (self.isCancelled) return;
    for (int i = 0; i < 10000; i++) {
        NSLog(@"%i - %@",i,NSThread.currentThread);
    }
    
    if (self.isCancelled) return;
    for (int i = 10000; i < 20000; i++) {
        NSLog(@"%i - %@",i,NSThread.currentThread);
    }
    
    if (self.isCancelled) return;
    for (int i = 20000; i < 30000; i++) {
        NSLog(@" %i - %@",i,NSThread.currentThread);
    }
    
    if (self.isCancelled) return;
    for (int i = 30000; i < 40000; i++) {
        NSLog(@"%i - %@",i,NSThread.currentThread); }}@end
Copy the code
#import "ViewController.h"
#import "ZYOperation.h"

@interface ViewController(a)

@property (nonatomic ,strong) NSOperationQueue *queue;

@end

@implementation ViewController

/ /
- (IBAction)startClick {
     //[self start1];
  	 [self start2];
}

/ / pause
- (IBAction)suspendClick {
    // Only the operations following the current operation can be paused. The current operation is indivisible and must be completed
    // Operations are stateful
    self.queue.suspended = YES;
}

/ / recovery
- (IBAction)resumeClick {
    self.queue.suspended = NO;
}

/ / cancel
- (IBAction)cancelClick {
    // The difference between a pause and a cancel is that you can resume the operation after the pause. When the operation is cancelled, all the operations are cleared and no further operations can be performed.
    // Cancel Cancel all operations
    // You can only cancel operations in the queue that process the wait state
    [self.queue cancelAllOperations];
}

- (void)start1 {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 10000; i++) {
            NSLog(@"1 ~ %i - %@",i,[NSThreadcurrentThread]); }}];NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 10000; i++) {
            NSLog(@"2 ~ %i - %@",i,[NSThreadcurrentThread]); }}];NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 10000; i++) {
           NSLog(@"3 ~ %i - %@",i,[NSThreadcurrentThread]); }}];NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 10000; i++) {
             NSLog(@"4 ~ %i - %@",i,[NSThreadcurrentThread]); }}]; [queue addOperations:@[op1 ,op2, op3, op4] waitUntilFinished:NO];
    
    self.queue = queue;
}

- (void)start2 {
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;
    
    ZYOperation *op = [[ZYOperation alloc] init];
    
    [queue addOperation:op];
    
    self.queue = queue;
}
@end
Copy the code

Basic concepts of iOS multithreading

NSThread for iOS Multithreading

NSOperation for iOS Multithreading

GCD for iOS Multithreading