NSOperation profile
NSOperation and NSOperationQueue are multi-threaded solutions provided by Apple. In fact, NSOperation and NSOperationQueue are based on a higher level of ENCAPSULATION of GCD and are fully object-oriented. But it’s easier to use and more readable than GCD
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, NSOperationQueue Operation and operation queue
Since it is a higher level of encapsulation based on GCD. Then, some of the concepts in GCD also apply to NSOperation and NSOperationQueue. There are similar concepts of tasks (operations) and queues (operation queues) in NSOperation and NSOperationQueue.
- Operation (Operation)
- By executing an operation, in other words, that piece of code that you execute in a thread.
- In GCD it is placed in a block. In NSOperation, we use NSOperation subclasses NSInvocationOperation, NSBlockOperation, or custom subclasses to encapsulate operations.
- Operation Queues
- The queue here refers to the operation queue, which is used to store operations. This is different from the FIFO (first in, first out) principle of scheduling queues in GCD. NSOperationQueue For operations added to the queue, the ready state is first entered (the ready state depends on the dependencies between the operations), and the start (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).
- Operation queue by setting the maximum number of concurrent operation (maxConcurrentOperationCount) to control the concurrency, serial.
- NSOperationQueue provides us with two different types of queues: primary queue and custom queue. The main queue runs on top of the main thread, while custom queues execute in the background.
- 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 and NSOperationQueue are basically used
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
- Custom subclasses of NSOperation that encapsulate operations by implementing internal methods.
Use subclass NSInvocationOperation
/** NSInvocationOperation : - (void)demo{//1: NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"123"]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //3: the operation is queued -- the operation will be queued in a new thread [queue addOperation:op]; } - (void)handleInvocation:(id)op{ NSLog(@"% @ % @" -- -,op,[NSThread currentThread]);
}
Copy the code
Print:
2020-01-29 15:43:11.012492+0800 001-- <NSThread: 0x600001ea9080>{number = 3, name = (null)}Copy the code
NSInvocationOperation can also be invoked manually so that no thread is started and the task is executed on the current queue
*/ - (void)demo1{NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"123"]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSOperationQueue *queue = [NSOperationQueue alloc] init]; // [queue addOperation:op]; // Manually set up operation, default on current thread [op start]; } - (void)handleInvocation:(id)op{ NSLog(@"% @ % @" -- -,op,[NSThread currentThread]);
}
Copy the code
Print:
2020-01-29 15:52:55.404726+0800 001-- <NSThread: 0x60000024e940>{number = 1, name = main}Copy the code
Note: We try not to execute tasks in this way, as it will crash if the task is already queued
Use the subclass NSBlockOperation
/** blockOperation Initial experience */ - (void)demo2{//1: create blockOperation NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@ -- %@",[NSThread mainThread],[NSThread currentThread]); }]; Bo.pletionblock = ^{NSLog(@)"完成了!!!"); }; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //3: Add to queue [queue addOperation:bo]; }Copy the code
Print:
2020-01-29 15:57:30.760964+0800 001-- NSOperation experience [1812:85696] <NSThread: 0x600001a66940>{number = 1, name = (null)} -- <NSThread: 0x600001A2DEC0 >{number = 3, name = (NULL)} 2020-01-29 15:57:7.761098 +0800 001-- NSOperation [1812:85697]Copy the code
NSBlockOperation can also be performed manually using the start method so that no thread is started and the task is executed on the current queue. If the task has already been added to the queue, manually calling up the task will crash
Custom subclasses of NSOperation that encapsulate operations by implementing internal methods.
If subclasses NSInvocationOperation and NSBlockOperation are not sufficient for your daily needs, you can use custom subclasses that inherit from NSOperation.
Define a subclass that inherits NSOperation and override the main method.
// NHOperation.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NHOperation : NSOperation
@end
NS_ASSUME_NONNULL_END
// NHOperation.m
#import "NHOperation.h"
@implementation NHOperation
- (void)main{
if(! self.isCancelled) {for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1 - % @", [NSThread currentThread]);
}
}
}
@end
Copy the code
Import the header file nhOperation.h
- (void)demo3{
NHOperation *nhQueue = [[NHOperation alloc]init];
[nhQueue start];
}
Copy the code
Print:
2020-01-29 16:20:57.036713+0800 001-- <NSThread: 0x600001c16940>{number = 1, Name = main} 2020-01-29 16:20:59.037879+0800 001-- <NSThread: 0x600001c16940>{number = 1, name = main}Copy the code
This custom inherited NSOperation writing method can master the life cycle of the thread by itself. SDWebImage framework rewrites a series of methods such as start and main to download the image module
Executable code block
/** Executable code block */ - (void)demo5{// Create operation NSBlockOperation *ob = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"% @",[NSThread currentThread]); }]; // Add execution code block [ob addExecutionBlock:^{NSLog(@)"This is an executable code block - %@",[NSThread currentThread]); }]; // execute code block in new thread created operation in current thread //[ob start]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:ob]; }Copy the code
Print:
2020-01-29 16:37:49.565971+0800 001-- NSThread: <NSThread: 0x600002457d40>{number = 3, Name = (null)} 2020-01-29 16:37:49.565991+0800 001- <NSThread: 0x600002458480>{number = 4, name = (null)}Copy the code
Interthread communication
/** thread communication */ - (void)demo6{NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.name = @"Noah";
[queue addOperationWithBlock:^{
NSLog(@"% @ = % @",[NSOperationQueue currentQueue],[NSThread currentThread]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"% @, % @",[NSOperationQueue currentQueue],[NSThread currentThread]);
}];
}];
}
Copy the code
Print:
2020-01-29 16:38:41.372075+0800 001-- NSOperation Initial experience [2239:102618] <NSOperationQueue: 0x6000015EE560 >{name ='Noah'} = <NSThread: 0x6000000f9880>{number = 3, Name = (null)} 2020-01-29 16:38:41.378217+0800 001-- NSOperation first experience [2239:102581] <NSOperationQueue: 0x6000015ea180>{name ='NSOperationQueue Main Queue'} --<NSThread: 0x600000086940>{number = 1, name = main}
Copy the code
Priority setting
*/ - (void)demo4{NSBlockOperation *bo1 = [NSBlockOperation. */ - (void)demo4{NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 10; i++) {
NSLog(@"First operation %d -- %@", i, [NSThread currentThread]); }}]; / / set the highest priority - bo1. QualityOfService = NSQualityOfServiceUserInteractive; // create a second operation NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 10; i++) {
NSLog(@"Second operation %d -- %@", i, [NSThread currentThread]); }}]; / / set the lowest priority - bo2. QualityOfService = NSQualityOfServiceBackground; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //3: Add to queue [queue addOperation:bo1]; [queue addOperation:bo2]; }Copy the code
Print:
2020-01-29 16:42:21.830886+0800 001-- <NSThread: 0x6000003f7200>{number = 3, Name = (null)} 2020-01-29 16:42:21.830901+0800 001-- <NSThread: 0x600000384a80>{number = 4, Name = (null)} 2020-01-29 16:42:21.831015+0800 001-- <NSThread: 0x6000003f7200>{number = 3, Name = (null)} 2020-01-29 16:42:21.831076+0800 001-- <NSThread: 0x6000003f7200>{number = 3, Name = (null)} 2020-01-29 16:42:21.831107+0800 001-- <NSThread: 0x600000384a80>{number = 4, Name = (null)} 2020-01-29 16:42:21.831135+0800 001-- <NSThread: 0x6000003f7200>{number = 3, Name = (null)} 2020-01-29 16:42:21.831217+0800 001-- <NSThread: 0x6000003f7200>{number = 3, Name = (null)} 2020-01-29 16:42:21.831241+0800 001-- <NSThread: 0x600000384a80>{number = 4, Name = (null)} 2020-01-29 16:42:21.831306+0800 001-- <NSThread: 0x6000003f7200>{number = 3, Name = (null)} 2020-01-29 16:42:21.831351+0800 001-- <NSThread: 0x600000384a80>{number = 4, Name = (null)} 2020-01-29 16:42:21.831387+0800 001-- <NSThread: 0x6000003f7200>{number = 3, Name = (null)} 2020-01-29 16:42:21.831438+0800 001-- <NSThread: 0x600000384a80>{number = 4, Name = (null)} 2020-01-29 16:42:21.831595+0800 001-- <NSThread: 0x6000003f7200>{number = 3, Name = (null)} 2020-01-29 16:42:21.831771+0800 001-- <NSThread: 0x6000003f7200>{number = 3, Name = (null)} 2020-01-29 16:42:21.831957+0800 001-- <NSThread: 0x600000384a80>{number = 4, Name = (null)} 2020-01-29 16:42:21.834469+0800 001-- <NSThread: 0x6000003f7200>{number = 3, Name = (null)} 2020-01-29 16:42:21.834487+0800 001-- <NSThread: 0x600000384a80>{number = 4, Name = (null)} 2020-01-29 16:42:21.834579+0800 001-- <NSThread: 0x600000384a80>{number = 4, Name = (null)} 2020-01-29 16:42:21.834659+0800 001-- <NSThread: 0x600000384a80>{number = 4, Name = (null)} 2020-01-29 16:42:21.834761+0800 001-- <NSThread: 0x600000384a80>{number = 4, name = (null)}Copy the code
Example Set the maximum number of concurrent requests in a queue
- (void)demo7{ NSOperationQueue *queue = [NSOperationQueue new]; / / set maximum concurrency is 2 queue queue maxConcurrentOperationCount = 2;for (int i = 0; i<10; i++) {
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"%d-%@",i,[NSThread currentThread]); }]; }}Copy the code
Print:
[2360:107175] 0-<NSThread: 0x6000023c2140>{number = 3, Name = (null)} 2020-01-29 16:45:11.512505+0800 001-- NSThread: 0x6000023d1280>{number = 4, Name = (null)} 2020-01-29 16:45:13.514845+0800 001-- NSThread: 0x6000023c4980>{number = 5, Name = (NULL)} 2020-01-29 16:45:13.514883+0800 001-- Initial NSOperation [2360:107174] 3-<NSThread: 0x6000023fc380>{number = 6, name = (null)} 2020-01-29 16:45:15.516537+0800 001-- NSThread: 0x6000023d1280>{number = 4, Name = (null)} 2020-01-29 16:45:15.516537+0800 001-- NSThread: 0x6000023c4980>{number = 5, Name = (null)} 2020-01-29 16:45:17.518188+0800 001-- NSThread: 0x6000023d1280>{number = 4, Name = (NULL)} 2020-01-29 16:45:17.518188+0800 001-- Initial NSThread [2360:107174] 6-<NSThread: 0x6000023fc380>{number = 6, name = (null)} 2020-01-29 16:45:19.521176+0800 001-- NSThread: 0x6000023c4980>{number = 5, Name = (NULL)} 2020-01-29 16:45:19.521176+0800 001-- NSThread: 0x6000023fc380>{number = 6, name = (null)}
Copy the code
Dependencies between tasks
- (void)demo{NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5]; NSLog(@"Request token"); }]; NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5]; NSLog(@"Take the token, request data 1."); }]; NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5]; NSLog(@"Take data one, request data two."); }]; [bo2 addDependency:bo1]; [bo3 addDependency:bo2]; [bo1 addDependency:bo3]; //[bo1 addDependency:bo3]; //waitUntilFinished Block the thread [self.queue addOperations:@[bo1,bo2,bo3]waitUntilFinished:NO];
NSLog(@"Done? I'm going to do something else.");
}
Copy the code
Print:
2020-01-29 16:47:41.371621+0800 002-- NSOperation [2422:109106] 2020-01-29 16:47:42.374790+0800 --NSOperation [2422:109157] request token 2020-01-29 16:47:42.374790+0800 020-01-29 16:47:42.877750+0800 002-- NSOperation [2422:109156] Take data one, request data twoCopy the code
Suspend NSOperationQueue, go ahead, cancel
/** Suspend operationQueue, continue, cancel */ - (void)demo1{self.queue. Name = @"Cooci";
self.queue.maxConcurrentOperationCount = 2;
for(int i=0; i< 20; I++) {[self.queue addOperationWithBlock:^{[NSThread sleepForTimeInterval:1.0]; NSLog(@"%@-----%d",[NSThread currentThread],i);
}];
}
}
- (IBAction)pauseOrContinue:(id)sender {
if (self.queue.operationCount == 0) {
NSLog(@"There is no current operation, no need to suspend and continue.");
return; } // An action being performed by a detail cannot be suspendedif (self.queue.suspended) {
NSLog(@"Currently suspended, ready to continue.");
}else{
NSLog(@"Currently in execution state, ready to suspend"); } self.queue.suspended = ! self.queue.isSuspended; } - (IBAction)cancel:(id)sender { [self.queue cancelAllOperations]; }Copy the code
Print:
2020-01-29 16:52:44.854415+0800 002-- NSOperation <NSThread: 0x600001d57180>{number = 3, Name = (null)}-----1 2020-01-29 16:52:44.854415+0800 002-- NSOperation [2488:111998] 0x600001d1da40>{number = 4, Name = (null)}-----0 2020-01-29 16:52:45.856950+0800 002-- NSOperation [2488:111999] 0x600001d1f080>{number = 5, -----2 2020-01-29 16:52:45.856988+0800 002-- NSOperation [2488:112000] <NSThread: 0x600001d04280>{number = 6, Name = (NULL)}-----3 2020-01-29 16:52:46.277210+0800 002-- NSOperation [2488:111955] The NSOperation is in the execution state and is ready to suspend 2020-01-29 [2488:112001] <NSThread: 0x600001d57180>{number = 3, -----4 2020-01-29 16:52:46.857782+0800 002-- NSOperation [2488:111999] <NSThread: 0x600001d1f080>{number = 5, Name = (null)}-----5 2020-01-29 16:52:48.967113+0800 002-- NSOperation [2488:111955] is currently suspended, ready to continue 2020-01-29 16:52:49.968072+0800 002-- NSOperation [2488:112001] 0x600001d57180>{number = 3, Name = (null)}-----6 2020-01-29 16:52:49.968087+0800 002-- NSOperation [2488:111999] 0x600001d1f080>{number = 5, -----7 2020-01-29 16:52:50.969946+0800 002-- NSOperation [2488:112000] <NSThread: 0x600001d04280>{number = 6, Name = (null)}-----8 2020-01-29 16:52:50.969946+0800 002-- NSOperation [2488:112001] 0x600001d57180>{number = 3, Name = (null)}-----9 2020-01-29 16:52:51.465420+0800 002-- NSOperation [2488:111955 16:52:51.973286+0800 002-- NSThread: 0x600001d1f080>{number = 5, Name = (null)}-----10 2020-01-29 16:52:51.973297+0800 002-- NSOperation [2488:112001] 0x600001d57180>{number = 3, name = (null)}-----11Copy the code
Refer to the article
IOS multithreading: summary of NSOperation, NSOperationQueue