Preamble: It’s better to read in order
Concepts section – Processes and threads, tasks and queues
GCD for iOS Multithreading
NSOperation for iOS Multithreading
An introduction to the
NSOperation is a wrapper for GCD. GCD only supports FIFO queues. NSOpration can set maximum concurrency, set priority, add dependencies, and even set dependencies across queues
NSOperatio has two core concepts :NSOperation and NSOperationQueue. NSOperation is an abstract class. It depends on subclasses NSInvocationOperation, NSBlockOperation, and custom NSOperation.
Two Basic use
1. NSInvocationOperation
① Basic use
- (void)invocationOperation {// Handle transaction NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"a"]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // Create a queue. [queue addOperation:op]; }Copy the code
(2) Directly process transactions without adding hidden queues
- (void)invocationOperation {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"a"];
[op start];
}
Copy the code
③ Wrong use
- (void)invocationOperation { NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"a"]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:op]; [op start]; } -------------------- error log: ------------------- something is trying to start the receiver simultaneously from more than one thread' -------------------- Error log: -------------------Copy the code
The above code crashes because of the thread lifecycle:
[queue addOperation:op] Has queued the operation task that handles the transaction and has the thread run. [op start] Running the already running thread again causes thread chaos
- NSBlockOperation
The difference between NSInvocationOperation and NSBlockOperation is:
The former is similar to the target form; The latter is similar to the block form — functional programming, where the business logic code is more readable
- (void)blockOperation {// Initialize add transaction NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{ NSLog (@ "task 1 -- - % @", [NSThread currentThread]);}]; [bo addExecutionBlock:^{NSLog(@" task 2————%@",[NSThread currentThread]);}]; [bo addExecutionBlock:^{NSLog(@" task 3————%@",[NSThread currentThread]);}]; [bo addExecutionBlock:^{NSLog(@" task 4————%@",[NSThread currentThread]);}]; [bo addExecutionBlock:^{NSLog(@" task 5————%@",[NSThread currentThread]);}]; // Callback listener bo.completionBlock = ^{NSLog(@"completionBlock executed, all tasks complete "); }; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:bo]; NSLog(@" transaction added to NSOperationQueue"); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the transaction and adding NSOperationQueue task 1 -- -- -- < NSThread: 0x280Be7D00 >{number = 3, name = (null)} Task 4————<NSThread: 0x280Be7D00 >{number = 3, name = (null)} Task 3————<NSThread: 0x280bc0340>{number = 6, name = (null)} Task 2————<NSThread: 0x280BD8600 >{number = 5, name = (null)} Task 5————<NSThread: 0 x280be7d00 > {number = 3, name = (null)} completionBlock execution, the task is complete -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --Copy the code
The NSOperationQueue is executed asynchronously, so the completion order of task 12345 is uncertain, but the completionBlock callback is executed after the task is complete
You can also use it this way
- (void)blockOperation {// Initialize add transaction NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog (@ "task 1 -- - % @", [NSThread currentThread]);}]; NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" task 2————%@",[NSThread currentThread]);}]; NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" task 3————%@",[NSThread currentThread]);}]; NSBlockOperation *bo4 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" task 4————%@",[NSThread currentThread]);}]; // bo1 callback listener bo1.completionBlock = ^{NSLog(@" Bo1 completionBlock executed, task completed "); }; // bo2 callback listener bo2.completionBlock = ^{NSLog(@" Bo2 completionBlock executed, task completed "); }; // bo3 callback listener bo3.completionBlock = ^{NSLog(@"bo3 completionBlock executed, task completed "); }; // bo4 callback listener bo4.completionBlock = ^{NSLog(@"bo4 completionBlock executed, task completed "); }; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:bo1]; [queue addOperation:bo2]; [queue addOperation:bo3]; [queue addOperation:bo4]; NSLog(@" transaction added to NSOperationQueue"); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the transaction and adding NSOperationQueue task 1 -- -- -- < NSThread: 0x2834DD200 >{number = 7, name = (null)} Task 4————<NSThread: 0x2834D62C0 >{number = 4, name = (null)} Task 2————<NSThread: 0x2834DD7C0 >{number = 6, name = (null)} Task 3————<NSThread: 0x2834d6fc0>{number = 5, Name = (null)} The completionBlock of BO4 is executed, the completionBlock of BO1 is executed, the completionBlock of BO3 is executed, and the task is completed CompletionBlock execution of task -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --Copy the code
3 Set the priority
- (void)blockOperation { NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 5; I++) {/ / sleep (1); the NSLog (@ "first operation % d - % @", I, [NSThread currentThread]);}}]; / / set the highest priority bo1. QualityOfService = NSQualityOfServiceUserInteractive; NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{for (int I = 0; I < 5; I ++) {NSLog(@" second operation %d -- %@", i, [NSThread currentThread]); } }]; / / set the lowest priority bo2. QualityOfService = NSQualityOfServiceBackground; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:bo1]; [queue addOperation:bo2]; }Copy the code
Setting NSOperation to a higher priority only makes it more likely that the CPU will call it. It doesn’t mean that setting NSOperation to a higher priority means that it will all get done first
- Don’t use sleep – high priority task 1 precedes low priority task 2
- Use sleep for latency – high priority task 1 is slower than low priority task 2
4 Communication between threads
Use asynchrony in GCD to make network requests and then go back to the main thread to refresh UI.NSOperation also has operations similar to those used to communicate between threads
- (void)operationQueue { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.name = @"Felix"; [queue addOperationWithBlock:^{NSLog(@" request network %@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]); [[NSOperationQueue mainQueue] addOperationWithBlock:^{NSLog(@" refresh UI%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]); }]; }]; }Copy the code
5 Set the concurrency
In the GCD can only use semaphore to set the concurrency, and NSOperation can be set directly by setting maxConcurrentOperationCount concurrency
- (void)operationQueue { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.name = @"a"; queue.maxConcurrentOperationCount = 2; for (int i = 0; i < 5; I ++) {[queue addOperationWithBlock:^{// a task [NSThread sleepForTimeInterval:2]; NSLog(@"%d-%@", I,[NSThread currentThread]); }]; }}Copy the code
6 Adding a Dependency
Adding dependencies via addDependency in NSOperation can control the sequence of tasks executed
- (void)operationQueue { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5]; NSLog(@" request token");}]; NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5]; NSLog(@" hold token, request data 1");}]; NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5]; NSLog(@" hold data 1, request data 2");}]; [bo2 addDependency:bo1]; [bo3 addDependency:bo2]; [self.queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:YES]; NSLog(@" all done "); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the request token with a token, the request data with data 1, 2 all the request data is performed -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the output: -------------------Copy the code
7. Suspend, continue, cancel tasks
// Suspended queue. Suspended = YES; Queue. suspended = NO; // Cancel [queue cancelAllOperations];Copy the code
- The pause operation cannot suspend the current task in the execution state, but the task execution ends. Subsequent tasks will not be executed and are in the queuing state. Instead, the task is executed, and subsequent tasks are not executed and are in the queuing state.
- The cancel operation is similar to the pause operation. The current task is not cancelled immediately, but all subsequent tasks will never be executed again, and the operation cannot be resumed
3 User-defined NSOperation cache mechanism
According to SDWebImage loading network image cache mechanism, NSOperation custom image cache (local cache + memory cache)
- If there is data in memory, the picture is retrieved from memory and displayed
- If there is data in the sandbox, the picture is taken from the sandbox to display and stored in memory
- If neither, an asynchronous download writes the image data to both a local cache and an in-memory cache
-(void)simulationCacheImage{ UIImage *cacheImage = self.imageCacheDict[model.imageUrl]; If (cacheImage) {NSLog(@" get image from memory :%@", model.title); cell.imageView.image = cacheImage; return cell; } UIImage *diskImage = [UIImage imageWithContentsOfFile:[model.imageUrl getDowloadImagePath]]; If (diskImage) {NSLog(@" get image from sandbox :%@",model.title); cell.imageView.image = diskImage; [self.imageCacheDict setValue:diskImage forKey:model.imageUrl]; return cell; } NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" download image :%@", Model. The title); / / delay NSData * data = [NSData dataWithContentsOfURL: imageURL]; UIImage * image = [UIImage [self.imagecacheDict setValue:image forKey: model.imageurl]; [self.imagecacheDict setValue:image forKey: model.imageurl] WriteToFile :[model.imageurl getDowloadImagePath] Atomically :YES]; // Update UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ cell.imageView.image = image; }]; }]; [self.queue addOperation:bo]; }Copy the code