Why use NSOperation, NSOperationQueue?
- You can add completed blocks of code to execute after the action is complete.
- Add dependencies between operations to easily control the order of execution.
- Set the priority of the operation.
- You can easily cancel an operation.
- Use KVO to observe changes in the execution status of operations: isExecuteing, isFinished, isCancelled.
NSOperation and NSOperationQueue usage procedure
NSOperation needs to work with NSOperationQueue to implement multithreading. Because by default NSOperation is used alone and the system executes operations synchronously, we can better implement asynchronous execution with NSOperationQueue. NSOperation implements multithreading in three steps:
- Create operation: Encapsulate the operation to be performed in an NSOperation object.
- Create a queue: Create an NSOperationQueue object.
- Add operations to the queue: Add the NSOperation object to the NSOperationQueue object.
After that, the system will automatically pull the NSOperation out of the NSOperationQueue and execute the operation in the new thread.
NSOperation is an abstract class and cannot be used to encapsulate operations. We only use a subclass of it to encapsulate the operation. We have three ways to encapsulate operations.
- Use subclass NSInvocationOperation
- Use the subclass NSBlockOperation
- A custom subclass derived from NSOperation that encapsulates operations by implementing corresponding methods internally.
In the case of using NSOperation alone without using NSOperationQueue, the system executes operations synchronously. Let’s learn three ways to create the following operations.
NSInvocationOperation
In the case that an operation is performed in the main thread using subclass NSInvocationOperation alone without using NSOperationQueue, the operation is performed in the current thread and no new thread is started.
/** * use subclass NSInvocationOperation */
- (void)useInvocationOperation {
// 1. Create the NSInvocationOperation object
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
// 2. Call the start method to start the operation
[op start];
}
/** * task 1 */
- (void)task1 {
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
NSLog(1 - % @ "@"[NSThread currentThread]); // Prints the current thread}}Copy the code
NSBlockOperation
In the case that an operation is not performed using NSOperationQueue, using NSBlockOperation alone in the main thread, the operation is performed in the current thread and no new thread is started. In general, if an NSBlockOperation object encapsulates multiple operations. NSBlockOperation Whether to start a new thread depends on the number of operations. If the number of operations added is large, a new thread is automatically started. Of course, the number of open threads is determined by the system.
/** * use subclass NSBlockOperation */
- (void)useBlockOperation {
1. Create an NSBlockOperation object
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
NSLog(1 - % @ "@"[NSThread currentThread]); // Prints the current thread}}];// 2. Add additional operations
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
NSLog(2 - % @ "@"[NSThread currentThread]); // Prints the current thread}}];// 3. Call the start method to start the operation
[op start];
}
Copy the code
Custom NSOperation
NSOperationQueue controls serial and concurrent execution
Here is a key attribute maxConcurrentOperationCount, called the maximum number of concurrent operation. Used to control how many operations on a particular queue can participate in concurrent execution. Note: maxConcurrentOperationCount control is not the number of concurrent threads, but a queue at the same time the maximum number of operations that can execute concurrently. And an operation doesn’t have to run in just one thread. Maximum number of concurrent operation: maxConcurrentOperationCount
maxConcurrentOperationCount
By default, the value is -1, indicating that concurrent execution is not restricted.maxConcurrentOperationCount
When 1, the queue is a serial queue. Only serial execution.maxConcurrentOperationCount
If the value is greater than 1, the queue is a concurrent queue. Of course, this value should not exceed the system limit. Even if you set a large value, the system will automatically adjust to min{self-set value, the default maximum value set by the system}.
Rely on
NSOperation provides three interfaces for managing and viewing dependencies.
- (void)addDependency:(NSOperation *)op;
Add a dependency that makes the current operation dependent on the completion of the operation op.- (void)removeDependency:(NSOperation *)op;
Remove a dependency to remove the dependency of the current operation on op.@property (readonly, copy) NSArray<NSOperation *> *dependencies;
Array of all action objects that are executed before the current operation begins.
/** * addDependency: */
- (void)addDependency {
1. Create a queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2. Create a vm
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
NSLog(1 - % @ "@"[NSThread currentThread]); // Prints the current thread}}];NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // Simulate time-consuming operations
NSLog(2 - % @ "@"[NSThread currentThread]); // Prints the current thread}}];// 3. Add dependencies
[op2 addDependency:op1]; Op2 depends on op1, then op2 depends on op1
// 4. Add the operation to the queue
[queue addOperation:op1];
[queue addOperation:op2];
}
Copy the code
priority
NSOperation provides the queuePriority property, which applies to operations in the same operation queue but not to operations in different operation queues. By default, all the operation of the newly created object is NSOperationQueuePriorityNormal priority. However, we can change the execution priority of the current operation in the same queue by using the setQueuePriority: method.
// Priority value
typedef NS_ENUM(NSInteger.NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = - 8 -L,
NSOperationQueuePriorityLow = 4 -L,
NSOperationQueuePriorityNormal = 0.NSOperationQueuePriorityHigh = 4.NSOperationQueuePriorityVeryHigh = 8
};
Copy the code
For operations added to the queue, the ready state is first entered (the ready state depends on the dependencies between the operations), and then the start execution order (non-end execution order) of the ready operations is determined by the relative priority of the operations (priority is an attribute of the operation object itself).