[iOS Development]<Multi-threading [3]-NSOperation&operation queue NSOperationQueue>

Preface

I studied GCD during the winter vacation. Learn NSOperation today. Both are multi-threaded packages. NSOperation and NSOperationQueue are higher-level packages based on GCD and are completely object-oriented. Compared with various complex API methods of GCD, its advantage is that it is more simple and practical, and the code is readable. Sex is higher.

NSOperation, NSOperationQueue operations, and operation queues

Well, some concepts from GCD also apply NSOperation、NSOperationQueue. NSOperation、NSOperationQueueThere are similar concepts of tasks (operations) and queues (operation queues) in .

  • Operation:
    • It's the code you execute in the thread在这里插入代码片
    • It is placed inside the block during GCD. In NSOperation, we use NSOperation subclasses NSInvocationOperation, NSBlockOperation, or custom subclasses to encapsulate operations.
  • Operation Queues
    • The operation queue here is similar to the operation queue in the operating system. It is a queue used to store operations. In GCD, the queue is based on the FIFO principle. NSOperationQueue has a ready state for operations in the queue . The order of execution of operations that enter the ready state is determined by the operation priority provided by NSOperationQueue. Priority is a property of the operation object itself
    • NSOperationQueue has a maximum concurrency attribute to control concurrency and serialization.
    • NSOperationQueue provides us with two different types of queues: main queue and custom queue. The main queue runs on the main thread, while the custom queue executes in the background

Use of NSOperation and NSOperationQueue

NSOperationNeed to cooperate NSOperationQueueto achieve multi-threading. Because by default, NSOperationthe system performs operations synchronously when used alone, and with NSOperationQueueus, we can better achieve asynchronous execution.

NSOperationand NSOperationQueuespecific steps to implement multithreading

  1. First encapsulate the operations that need to be performed into an NSOperationobject
  2. Then NSOperationadd the object NSOperationQueueto
  3. Put the extracted NSOperationencapsulated operation into a new thread for execution
  4. Next, the system will take out NSOperationQueuethe operations from the operation queue NSOperationand perform the operations in a new thread.

Create NSOperation (not used with queue)

  • ️NSOperation is an abstract class and does not have the ability to encapsulate operations. Its subclasses must be used.

There are three ways to use NSOperation subclasses

  • 1. NSInvocationOperation
  • 2. NSBlockOperation
  • 3. Customize class inheritance NSOperationand implement corresponding internal methods

Without using NSOperationQueue and using NSOperation alone, the system performs operations synchronously.

The first NSInvocationOperation

The first parameter: the target object self.
The second parameter: the name of the calling method.
The third parameter: the parameter nil that the previous method needs to accept.

  1. Create operations and encapsulate tasks
  2. Start execution
 // 1.创建 NSInvocationOperation 对象
    NSInvocationOperation *invOp1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
 
    // 2.调用 start 方法开始执行操作
    [invOp1 start];

Please add image description

/**
 * 任务1
 */
- (void)task1 {
    
    
    for (int i = 0; i < 2; i++) {
    
    
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        
       
        NSLog(@"1~~~%@", [NSThread currentThread]); // 打印当前线程
        
        ///可以看到:在没有使用 NSOperationQueue、在主线程中单独使用使用子类 NSInvocationOperation 执行一个操作的情况下,操作是在当前线程执行的,并没有开启新线程。
    }
}
  • The result of printing [NSThread currentThread] will be the description information of the current thread
  • The printed result shows the description information of the current thread, including the memory address of the thread object (0x123456789), the thread number (number = 1) and the thread name (name = Main).
  • It can be seen that when NSOperationQueue is not used and the subclass NSInvocationOperation is used alone to perform an operation in the main thread, the operation is performed on the current thread and no new thread is opened.

The second NSBlockOperation

- (void)useBlockOperation {
    
    
    NSBlockOperation *blkOp1 = [NSBlockOperation blockOperationWithBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
        [blkOp1 start];
    /// 可以看到:在没有使用 NSOperationQueue、在主线程中单独使用 NSBlockOperation 执行一个操作的情况下,操作是在当前线程执行的,并没有开启新线程。

}

When NSOperationQueue is not used and NSBlockOperation is used alone to perform an operation in the main thread, the operation is performed on the current thread and no new thread is opened.

addExecutionBlock

NSBlockOperation also provides a method addExecutionBlock:, through addExecutionBlock: you can add additional operations to NSBlockOperation. These operations (including those in blockOperationWithBlock) can be executed simultaneously (concurrently) in different threads. It is considered complete only when all relevant operations have finished executing.
If there are many operations added, the operations in blockOperationWithBlock: may also be executed in other threads (non-current threads). This is determined by the system. It does not mean that the operations added to blockOperationWithBlock: will definitely be executed in the current thread. . (You can use addExecutionBlock: to try adding a few more operations.

/**
 * 使用子类 NSBlockOperation
 * 调用方法 AddExecutionBlock:
 */
- (void)useBlockOperationAddExecutionBlock {
    
    

    // 1.创建 NSBlockOperation 对象
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];

    // 2.添加额外的操作
    [op addExecutionBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [op addExecutionBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [op addExecutionBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"4---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [op addExecutionBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"5---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [op addExecutionBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"6---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [op addExecutionBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"7---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [op addExecutionBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"8---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];

    // 3.调用 start 方法开始执行操作
    [op start];
    
    /**
  
     */
}

@end

Please add image description

  • It can be seen that when using the subclass NSBlockOperation and calling the method AddExecutionBlock:, the operations in the blockOperationWithBlock: method and the operations in addExecutionBlock: are executed asynchronously in different threads. Moreover, the operations in the blockOperationWithBlock: method in this execution result are not executed in the current thread (main thread). This confirms that the operations in blockOperationWithBlock: may also be executed in other threads (non-current threads).
  • In general, if an NSBlockOperation object encapsulates multiple operations. Whether NSBlockOperation starts a new thread depends on the number of operations. If the number of added operations is large, a new thread will be automatically started. Of course, the number of threads enabled is determined by the system.

Use a custom subclass inherited from NSOperation

If using subclasses NSInvocationOperation and NSBlockOperation cannot meet daily needs, we can use custom subclasses inherited from NSOperation. You can define your own NSOperation object by overriding the main or start method.
Overriding the main method is relatively simple. We do not need to manage the status attributes of the operation isExecuting and isFinished. When main returns from execution, the operation ends.
First define a subclass inherited from NSOperation and override the main method.

#import "TestOperation.h"

@implementation TestOperation
- (void)main {
    
    
    if (!self.isCancelled) {
    
    
            for (int i = 0; i < 2; i++) {
    
    
                [NSThread sleepForTimeInterval:2];
                NSLog(@"1---%@", [NSThread currentThread]);
            }
        }
}
@end
**加粗样式**

Please add image description

In the NSOperation class, isCancelled is a Boolean property used to check whether the operation was canceled. When you cancel an operation, the isCancelled property will be set to YES, indicating that the operation has been cancelled.

In the main method of the TestOperation class, you can determine whether the operation has been canceled by checking the isCancelled property. In the example, if the operation is not canceled, a simple loop is executed and information about the current thread is printed in each loop.

In each loop, use the [NSThread sleepForTimeInterval:] method to pause the current thread for 2 seconds, and then print the thread information. This can simulate a time-consuming task. But before each loop starts, the isCancelled property is checked to ensure that the operation has not been canceled.

By using the isCancelled property, we can check the cancellation status during the execution of the operation so that the execution of the operation can be terminated at the appropriate time. This helps you handle cleanup and logic control when canceling operations.

  • It can be seen that when NSOperationQueue is not used and the main thread uses a custom subclass inherited from NSOperation alone, the operation is performed on the main thread and no new thread is opened.

Create operation queue NSOperationQueue

NSOperationQueue has two queues: main queue and custom queue. The custom queue includes both serial and concurrent functions. The following are the basic creation methods and characteristics of the main queue and custom queue.

  • Main queue: All operations added to the main queue will be executed in the main thread.
// 主队列获取方法
NSOperationQueue *queue = [NSOperationQueue mainQueue];

  • Custom queue: All operations added to the custom queue will be executed in child threads. The custom queue also includes serial and concurrent functions.
// 自定义队列创建方法
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

Add the operation to the queue

Add the created operation to the queue. There are two methods in total:

  1. (void)addOperation:(NSOperation *)op;
    • You need to create an operation first, and then add the created operation to the created queue.
/**
 * 使用 addOperation: 将操作加入到操作队列中
 */
- (void)addOperationToQueue {
    
    

    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2.创建操作
    // 使用 NSInvocationOperation 创建操作1
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];

    // 使用 NSInvocationOperation 创建操作2
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];

    // 使用 NSBlockOperation 创建操作3
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [op3 addExecutionBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"4---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];

    // 3.使用 addOperation: 添加所有操作到队列中
    [queue addOperation:op1]; // [op1 start]
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]
}

Use the NSOperation subclass to create an operation, and use addOperation: to add the operation to the operation queue to start a new thread for concurrent execution.

  1. - (void)addOperationWithBlock:(void (^)(void))block;
  • There is no need to create an operation first, add the operation in the block, and directly add the block containing the operation to the queue.
/**
 * 使用 addOperationWithBlock: 将操作加入到操作队列中
 */

- (void)addOperationWithBlockToQueue {
    
    
    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2.使用 addOperationWithBlock: 添加操作到队列中
    [queue addOperationWithBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [queue addOperationWithBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [queue addOperationWithBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
}

Use addOperationWithBlock: After adding the operation to the operation queue, you can start a new thread for concurrent execution.

NSOperationQueue controls serial execution and concurrent execution

The custom queue created by NSOperationQueue has both serial and concurrent functions. Above we demonstrated the concurrent function, so how is its serial function implemented?

Property maxConcurrentOperationCount, called the maximum number of concurrent operations. Used to control how many operations in a specific queue can participate in concurrent execution at the same time.

  • Here maxConcurrentOperationCount controls not the number of concurrent threads, but the maximum number of operations that can be executed concurrently in a queue. And an operation does not only run in one thread.

  • Maximum number of concurrent operations:maxConcurrentOperationCount

    • maxConcurrentOperationCountBy default, it is -1, which means no restriction and concurrent execution is possible.
    • maxConcurrentOperationCountWhen 1, the queue is a serial queue. Can only be executed serially.
    • maxConcurrentOperationCountWhen greater than 1, the queue is a concurrent queue. Operations are executed concurrently. Of course, this value should not exceed the system limit. Even if you set a large value, the system will automatically adjust to min {the value you set, the default maximum value set by the system}.

    Question record: Why does the file I open in Xcode’s command and tool not print the result but the ViewController prints the result?

    The reason is that the file I opened in Xcode's command and tools did not print the result and ended the program directly.Please add image description

Then print it in the viewController of a demo Please add image description
and find it. . .
Please add image description
testPlease add image description

The thread ends after printing only once.

Normal Process

Maxcount = 1
Please add image description

Maxcount = 2 or more
Please add image description
When the maximum number of concurrent operations is 1, operations are executed serially in order, and after one operation is completed, the next operation begins to execute. When the maximum number of concurrent operations is 2, the operations are executed concurrently, and two operations can be executed at the same time. The number of opened threads is determined by the system and does not require us to manage it.

NSOperation operation dependencies

The most attractive thing about NSOperation and NSOperationQueue is that it can add dependencies between operations. Through operation dependencies, we can easily control the order of execution between operations. NSOperation provides 3 interfaces for us to manage and view dependencies.

  • (void)addDependency:(NSOperation *)op;Add dependencies so that the current operation depends on the completion of operation op.
  • (void)removeDependency:(NSOperation *)op;Remove the dependency and cancel the dependence of the current operation on the operation op.
  • @property (readonly, copy) NSArray<NSOperation *> *dependencies; An array of all operation objects that have completed execution before the current operation begins.

Now consider this requirement, for example, there are two operations A and B. Only after A has completed the operation, B can perform the operation.

If you use dependency processing, then you need to make operation B depend on operation A. Indicates executing A first and then executing B

/**
 * 操作依赖
 * 使用方法:addDependency:
 */
- (void)addDependency {
    
    

    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2.创建操作
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
    
    
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];

    // 3.添加依赖
    [op2 addDependency:op1]; // 让op2 依赖于 op1,则先执行op1,在执行op2

    // 4.添加操作到队列中
    [queue addOperation:op1];
    [queue addOperation:op2];
}

Please add image description
By implementing dependencies, we can determine the order of execution.

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

  • @property (readonly, copy) NSArray<NSOperation *> *dependencies is a property of the NSOperation class, used to obtain all dependent operations of the current operation.
  • This property returns an NSArray object containing all dependent operations. Each element in the NSArray is an NSOperation object that represents the operation on which the current operation depends.
  • You can use this property to get all the dependent operations of the current operation and further operate on these dependencies. For example, you can iterate over a dependency array, check the status of dependency operations, or perform other operations.
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    
    
    NSLog(@"Operation 1");
}];
NSOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    
    
    NSLog(@"Operation 2");
}];
NSOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    
    
    NSLog(@"Operation 3");
}];

[operation2 addDependency:operation1];
[operation3 addDependency:operation2];

[queue addOperations:@[operation1, operation2, operation3] waitUntilFinished:NO];

// 获取operation2的依赖操作
NSArray<NSOperation *> *dependencies = operation2.dependencies;

// 遍历依赖操作
for (NSOperation *dependency in dependencies) {
    
    
    // 执行操作,检查状态等
    NSLog(@"Dependency: %@", dependency);
}

Before printing thread 2, the dependency of thread 2 is obtained, which is thread one. The print result shows the class and memory address of operation1.
Please add image description

NSOperation priority

NSOperation provides queuePriority(priority) attributes that queuePriorityapply to operations in the same operation queue but not to operations in different operation queues. By default, all newly created operands have priority NSOperationQueuePriorityNormal. But we can use setQueuePrioritythe: method to change the execution priority of the current operation in the same queue.

NSOperationQueuePriority is an enumeration type used to define the priority of the operation queue. It has the following values:

NSOperationQueuePriorityVeryLow:非常低优先级,值为 -8。
NSOperationQueuePriorityLow:低优先级,值为 -4。
NSOperationQueuePriorityNormal:普通优先级,值为 0。
NSOperationQueuePriorityHigh:高优先级,值为 4。
NSOperationQueuePriorityVeryHigh:非常高优先级,值为 8

These priority values ​​can be used to control the order in which operations are executed in the queue. Operations with higher priority will be executed before operations with lower priority, but there is no guarantee that they will be executed completely in priority order because they are also affected by other factors, such as operation dependencies, operation status, etc.

In NSOperationQueue, you can use the following methods to set the priority of operations:

  • setQueuePriority:: used to set the priority of a single operation.
  • setOperations:priority:: used to set the priority of multiple operations.
  • For operations added to the queue, they first enter the ready state (the ready state depends on the dependencies between operations), and then the starting execution order (non-ending execution order) of the operations that enter the ready state is determined by the relative priority between operations. Determined by level (priority is a property of the operation object itself).

When all dependencies of an operation have been completed, the operation object usually enters the ready state, waiting to be executed.

  • For example, there are now 4 operations with priority levels NSOperationQueuePriorityNormal (default level): op1, op2, op3, op4. Among them, op3 depends on op2, and op2 depends on op1, that is, op3 -> op2 -> op1. Now add these 4 operations to the queue and execute them concurrently.
    • Because op1 and op4 have no dependent operations, they are operations in a ready state before op1 and op4 are executed.
    • Both op3 and op2 have dependent operations (op3 depends on op2, op2 depends on op1), so op3 and op2 are not operations in the ready state.

After understanding the operation of entering the ready state, we understand the role of the queuePriority attribute.

  • queuePriorityProperty determines the order in which operations are started between operations that enter the ready state. Also, priorities do not replace dependencies.
  • If a queue contains both high-priority operations and low-priority operations, and both operations are ready, the queue executes the high-priority operation first. For example, in the above example, if op1 and op4 are operations of different priorities, the operation with higher priority will be executed first.
  • If a queue contains both ready operations and unready operations, the unready operations have a higher priority than the ready operations. Then, although the ready operation has a low priority, it will be executed first. Priorities do not replace dependencies. If you want to control the startup order between operations, you must use dependencies.

Communication between NSOperation and NSOperationQueue threads

Like GCD, we put the time-consuming tasks into other threads and return to the main thread when completed.

/**
 * 线程间通信
 */
- (void)communication {
    
    

    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];

    // 2.添加操作
    [queue addOperationWithBlock:^{
    
    
        // 异步进行耗时操作
        for (int i = 0; i < 2; i++) {
    
    
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }

        // 回到主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
    
    
            // 进行一些 UI 刷新等操作
            for (int i = 0; i < 2; i++) {
    
    
                [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
                NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
            }
        }];
    }];
}

Please add image description

Thread safety

Locking is usually used to ensure thread safety.

NSLock is a lock mechanism provided in the Foundation framework, used to protect critical section code in a multi-threaded environment. By using NSLock, you can ensure that only one thread can access the locked code block at the same time, thus avoiding data race and thread conflicts.

// 创建 NSLock 对象
NSLock *lock = [[NSLock alloc] init];

// 在多线程环境下加锁和解锁
[lock lock];  // 加锁

// 执行需要保护的临界区代码

[lock unlock];  // 解锁

In the above example, I first create an NSLock object lock. Then, in a multi-threaded environment, the lock is obtained by calling the [lock lock] method, which blocks other threads until the lock is available. After the critical section code is executed, call the [lock unlock] method to unlock, allowing other threads to acquire the lock and continue execution .

It should be noted that locking and unlocking operations should occur in pairs to ensure that critical sections are properly protected. When using NSLock, the following guidelines should be followed:

  • Use [lock lock] and [lock unlock] before and after the critical section code that needs to be protected to perform locking and unlocking operations.
  • Try to keep the execution time of the critical section code as short as possible to reduce the lock occupation time and improve the efficiency of multi-thread execution.
  • When using NSLock for locking, make sure that all threads accessing shared resources use the same NSLock object.
  • Using NSLock can effectively prevent data competition and thread conflicts in multi-threaded environments, and improve the concurrency and stability of applications.

NSOperation common properties and methods

Cancel operation method

  • (void)cancel;Cancellable operations essentially mark the isCancelled state.

How to determine operating status

  • (BOOL)isFinished; Determine whether the operation has ended.
  • (BOOL)isCancelled; Determine whether the operation has been marked for cancellation.
  • (BOOL)isExecuting; Determine whether the operation is running.
  • (BOOL)isReady; Determine whether the operation is in a ready state. This value is related to the dependency of the operation.

Operation synchronization

  • (void)waitUntilFinished; Blocks the current thread until the operation ends. Can be used to synchronize thread execution order.
  • (void)setCompletionBlock:(void (^)(void))block; completionBlockCompletionBlock will be executed when the current operation is completed.
  • (void)addDependency:(NSOperation *)op;Add dependencies so that the current operation depends on the completion of operation op.
  • (void)removeDependency:(NSOperation *)op; Remove the dependency and cancel the dependence of the current operation on the operation op.
    @property (readonly, copy) NSArray<NSOperation *> *dependencies;An array of all operation objects that completed execution before the current operation began.

NSOperationQueue common properties and methods

Cancel/pause/resume operation

  • (void)cancelAllOperations; Can cancel all operations on the queue.
  • (BOOL)isSuspended;Determine whether the queue is in a paused state. YES is the pause state, NO is the resume state.
  • (void)setSuspended:(BOOL)b; You can set the pause and resumption of the operation. YES represents pausing the queue and NO represents resuming the queue.

Operation synchronization

  • (void)waitUntilAllOperationsAreFinished;Blocks the current thread until all operations in the queue are executed.

add/get operations

  • (void)addOperationWithBlock:(void (^)(void))block; Add an NSBlockOperation type operation object to the queue.
  • (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; Add an operation array to the queue. The wait flag blocks the current thread until all operations are completed.
  • (NSArray *)operations; The array of operations currently in the queue (it will be automatically cleared from this array after an operation is executed).
  • (NSUInteger)operationCount; The number of operations in the current queue.

Get queue

  • (id)currentQueue; Gets the current queue, and returns nil if the current thread is not running on NSOperationQueue.
  • (id)mainQueue; Get the main queue

Multithreading comparison

Insert image description here

pthread

  • pthread is cross-platform, difficult to use, and requires manual management of the thread life cycle

GCD and NSOperation

  • GCD has higher execution efficiency. It executes tasks composed of Blocks. It is a lightweight data structure and is more convenient to write.
  • GCD only supports FIFO queues. NSOperation can adjust the execution order by setting the maximum number of concurrencies, setting priorities, and adding dependencies.
  • NSOperation can set dependencies across queues, and GCD can only control the execution order through fences and other methods.
  • NSOperation is more object-oriented, supports KVO, and can also add subclasses through inheritance and other relationships.
  • So if we need to consider the sequence and dependencies between asynchronous operations, such as multi-threaded concurrent downloads, etc., use NSOperation

The difference between GCD and NSThread

  • NSThread specifies the method to be executed through @selector. The code is scattered and relies on the NSObject classification to implement communication between threads. If you want to open a thread, you must create multiple thread objects. What is often used is [NSTread current] to view the current thread.
  • NSThread is an object that controls thread execution. It is not as abstract as NSOperation. Through it, we can easily get a thread and control it. But the concurrency control between NSThread threads needs to be controlled by ourselves, which can be achieved through NSCondition.
  • GCD specifies the code to be executed through blocks. The code is concentrated and all the codes are written together, making the code simpler, easier to read and maintain, and there is no need to manage the process of thread creation/destruction/reuse! Programmers do not need to care about the life cycle of threads

Guess you like

Origin blog.csdn.net/weixin_61639290/article/details/130753991