NSThread

NSThread is an object-oriented thread operation technology officially provided by Apple and the most basic multithreading class provided by Foundation framework. Each OBJECT of NSThread class represents a thread, which is simple and convenient. It can directly operate thread objects, but requires developers to control the life cycle of threads themselves, so it is rarely used in ordinary times.

The creation of NSThread

  1. throughinitInitialization mode create, (instantiation mode)
NSString *threadName = @"NSThread"; NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction:) object:threadName]; [thread start]; Use the selector of the target object as the task execution part of the thread.Copy the code
NSThread *thread = [[NSThread alloc] initWithBlock:^{ NSLog(@"NSThread..."); }]; [thread start]; Use blocks as the task execution part of the threadCopy the code

The instance method will return the NSThread object. When the thread needs to be started, the start method needs to be manually triggered. (The start thread method here only changes the state of the thread from new to ready, and when the task of executing the thread needs to be scheduled by the system.)

2. DetachNewThread create (class method create)

[NSThread detachNewThreadSelector:@selector(threadAction:) toTarget:self withObject:nil]; Use the selector of the target object as the task execution part of the thread.Copy the code
[NSThread detachNewThreadWithBlock:^{ NSLog(@"thread"); }]; Use blocks as the task execution part of the threadCopy the code

Class method, returns void, and starts the thread immediately after it is created.

3. Call NSObject’s performSelector: related method

Perform the task on the current thread - (id)performSelector:(SEL)aSelector; - (id)performSelector:(SEL)aSelector withObject:(id)object; Perform the task on the specified thread - (void)performSelector:(SEL)aSelector onThread:(NSThread *) THR withObject:(id)arg waitUntilDone:(BOOL)wait; In the main thread to perform a task - (void) performSelectorOnMainThread: (SEL) aSelector withObject: (id) arg waitUntilDone: (BOOL) wait;Copy the code

This method can be used for communication between threads.

Property method of NSThread

@property (class, readonly, strong) NSThread *currentThread; Class property that gets the current thread, returns the thread that was called from the main thread and returns the main thread object if it was called from the main thread, okayCopy the code
@property (class, readonly, strong) NSThread *mainThread; Class property that returns the main thread, regardless of which thread is calledCopy the code
@property double threadPriority; Set the priority of the thread. The value ranges from 0 to 1. The higher the number, the higher the priorityCopy the code

As we know, in the process of thread scheduling, the thread with higher priority is more likely to be selected to the execution state. However, we cannot judge the execution order of multiple threads only by relying on priority. The execution of thread tasks is also related to the resources of the task, so there is no way to predict the execution order of multiple threads.

@property (readonly, getter=isExecuting) BOOL executing; Determines whether the thread is executingCopy the code
@property (readonly, getter=isFinished) BOOL finished; Determines whether the thread is terminatedCopy the code
@property (readonly, getter=isCancelled) BOOL cancelled; Determines whether the thread has been canceledCopy the code
+ (BOOL)isMultiThreaded; Check whether it is multithreadedCopy the code
@PROPERTY (readonly) BOOL isMainThread Checks whether the current thread is the main threadCopy the code
+ (void)sleepUntilDate:(NSDate *)date; + (void)sleepForTimeInterval:(NSTimeInterval)ti; Let the thread sleep, immediately give up the current time slice, give up CPU resources, go into a blocking state whether it's the main thread or any other thread, as soon as you execute this method, the thread will sleepCopy the code
- (void)start; Starts the current new thread and changes the thread state to readyCopy the code
+ (void)exit; Exit current thread, which thread executes, which thread exitsCopy the code
- (void)cancel; Calling this method sets the Cancelled property to YES, but does not exit the threadCopy the code

Grand Central Dispatch (GCD)

GCD is apple’s multi-core parallel computing solution, one of the asynchronous task execution technology. The GCD automatically uses more CPU cores, and the GCD automatically creates threads, schedules tasks, destroys threads, and manages thread lifecycles. Developers simply define the tasks they want to execute and tell the GCD what they want to execute, and without writing any thread management code, the GCD can generate the necessary threads and plan to execute the tasks.

Before we look at GCD, let’s look at a few concepts:

  • Task: is the code to be executed in the thread, use this codeblockWrap it up, then add the task to the specified execution mode (synchronous and asynchronous), and waitCPUFetching a task from a queue (serial queue and concurrent queue) and placing it in the corresponding thread for execution.
  • Queue: A queue structure that holds threaded tasks. (system toFIFOTo schedule tasks in the queue for execution), inGCDThere are two types of queues: serial queues and concurrent queues.
  • Synchronous execution: In a thread, tasks are executed one after the other.
  • Asynchronous execution: multiple new threads are opened and tasks can be executed at the same time.
  • Serial queue: Threaded tasks are executed in the order in the queue.
  • Concurrent queues: Threaded tasks can be executed at the same time.

Synchronous and asynchronous execution

Synchronizing Tasks

Dispatch_sync (dispatch_get_global_queue(0, 0), ^{NSLog(@" synchronized tasks "); }); Executing a block task in the current thread does not open a new thread and must wait for the current code statement to complete before the next code statement is executed.Copy the code

Executing tasks asynchronously

Dispatch_async (dispatch_get_global_queue(0, 0), ^{NSLog(@" async task "); }); A thread is started to execute a block task, but not necessarily a new thread. The next statement can be executed without waiting for the current statement to complete.Copy the code

Serial and concurrent queues

The queue

As we know, queue in multi-threading refers to the queue structure storing thread tasks. Queue is a special linear list, which follows the first-in-first-out FIFO principle. When there is a new task, it will be added to the end of the queue.

Queue creation

Create a queue object using dispatch_queue_CREATE, passing in two parameters. The first parameter represents the unique identifier of the queue, which can be null. The second parameter is used to represent either a serial queue (DISPATCH_QUEUE_SERIAL) or a concurrent queue (DISPATCH_QUEUE_CONCURRENT).

Serial queues

Threaded tasks are executed in the order in the queue, that is, only one task is executed at a time, and the next task is executed after the previous task is completed.

dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
或者
dispatch_queue_t queue = dispatch_queue_create("serial_queue", NULL);
Copy the code

Concurrent queue

Multiple tasks can be executed concurrently, that is, multiple threads can be started to execute different tasks at the same time. There is no need to wait for the last task to complete.

dispatch_queue_t queue1 = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
Copy the code

In addition to these two types of queues, there are two special types of queues in the GCD: the primary queue and the global concurrent queue.

Primary queue The primary queue is a special serial queue provided in the GCD. It is mainly responsible for scheduling tasks on the main thread. No new threads are started and it depends on the main thread. Usually used when returning to the main thread to update the UI.

dispatch_queue_t mainQueue = dispatch_get_main_queue();
Copy the code

Global concurrent queue

The global concurrency queue is a concurrency queue and is the default concurrency queue provided by the GCD.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); The first parameter indicates the priority of the queue. The priority parameters are as follows: #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // High priority #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // Default (middle) priority #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // Low priority #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // Background priorityCopy the code

In general, the main queue is used in conjunction with the global concurrent queue:

Dispatch_async (dispatch_get_global_queue(0, 0), ^{dispatch_async(dispatch_get_main_queue()), ^{// return to main thread for UI}); });Copy the code

In general, the GCD has two ways of creating tasks: synchronous execution and asynchronous execution. Since global concurrent queues are concurrent queues, and the main queue depends on the main thread, the GCD can be viewed as having three kinds of queues: serial queues, concurrent queues, and main queues, so there are six different ways of executing tasks.

Execute + queue

1. Synchronous execution, serial queue

dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL); for (int i = 0; i<2; I ++) {dispatch_sync(queue, ^{NSLog(@" 1-%d-%@", I,[NSThread currentThread]); }); } for (int i = 0; i<2; I ++) {dispatch_sync(queue, ^{NSLog(@" 2-%d-%@", I,[NSThread currentThread]); }); }Copy the code

Tasks are executed sequentially under the current thread and no new thread is started. 2. Asynchronous execution, serial queue

NSLog(@"%@",[NSThread currentThread]); dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL); for (int i = 0; i<2; I ++) {dispatch_async(queue, ^{NSLog(@" 1-%d-%@", I,[NSThread currentThread]); }); } for (int i = 0; i<2; I ++) {dispatch_async(queue, ^{NSLog(@" 2-%d-%@", I,[NSThread currentThread]); }); }Copy the code

A new thread is started and tasks are executed sequentially under the current thread. 3. Synchronize the execution and create a queue

NSLog(@"%@",[NSThread currentThread]); dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i<2; I ++) {dispatch_sync(queue, ^{NSLog(@" concurrent 1-%d-%@", I,[NSThread currentThread]); }); } for (int i = 0; i<2; I ++) {dispatch_sync(queue, ^{NSLog(@" concurrent 2-%d-%@", I,[NSThread currentThread]); }); }Copy the code

No new thread is started and tasks are executed sequentially under the current thread. 4. Execute asynchronously and queue concurrently

NSLog(@"%@",[NSThread currentThread]); dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i<2; I ++) {dispatch_async(queue, ^{NSLog(@" 1-%d-%@", I,[NSThread currentThread]); }); } for (int i = 0; i<2; I ++) {dispatch_async(queue, ^{NSLog(@" 2-%d-%@", I,[NSThread currentThread]); }); }Copy the code

A new thread is started and tasks are executed sequentially.

5. Perform synchronization in the main queue

NSLog(@"%@",[NSThread currentThread]); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ for(int i=0; i<2; I++) {NSLog (@ "master queue synchronization 1 - % d - % @", I, [NSThread currentThread]); }}); NSLog(@"---%@",[NSThread currentThread]);Copy the code

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) Cause of deadlock: Since the main queue depends on the main thread, all tasks are executed in the main thread. First, there is an NSLog printing task, and then there is a block synchronization task. There are also NSLog printing tasks in the synchronization task, so the order of tasks in the queue is: NSLog task 1->block task ->NSLog task 2. At this time, because it is a synchronous queue, the block task needs to wait for NSLog task 2 to complete, while NSLog task 2 needs to wait for block task 2 to complete, which results in tasks waiting for each other and forming a deadlock.

6. Perform asynchronous execution in the primary queue

NSLog(@"%@",[NSThread currentThread]); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ for(int i=0; i<2; I++) {NSLog (@ "asynchronous 1 - % d - the home side columns % @", I, [NSThread currentThread]); }}); dispatch_async(queue, ^{ for(int i=0; i<2; I++) {NSLog (@ "asynchronous 2 - % d - the home side columns % @", I, [NSThread currentThread]); }});Copy the code

New threads are not started and tasks are executed sequentially in the main thread.

conclusion

The GCD API

1. The fence function dispatch_barrier_async

Sometimes two tasks need to be executed asynchronously, and only after the first task has been executed can the second task be executed. We would then need something like a fence to separate two asynchronously executed tasks, using the dispatch_barrier_async method to create a fence between the two task values.

dispatch_queue_t queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"1---%@",[NSThread currentThread]); }}); dispatch_barrier_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"barrier---%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"2---%@",[NSThread currentThread]); }});Copy the code

2. One-time method: dispatch_once

static dispatch_once_t onceToken; Dispatch_once (&onceToken, ^{// execute code}); Dispatch_once ensures that code in a block is executed only once during runtime. Code in a block is thread-safe by default and applies to simple interest mode.Copy the code

3. Delay execution: dispatch_after

NSLog(@"%@",[NSThread currentThread]); Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// after 2.0 seconds asynchronously append task code to main queue and start NSLog(@"after-- %@",[NSThread currentThread]); });Copy the code

4. Timing method:dispatch_source_t

It is mainly used for timing operations because it creates a timer that does not rely on RunLoop and has higher timing accuracy than NSTimer.

NSLog(@"%@",[NSThread currentThread]); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); Dispatch_source_set_timer (self timer, DISPATCH_TIME_NOW NSEC_PER_SEC 3.0 * and 0.1 * NSEC_PER_SEC); dispatch_source_set_event_handler(self.timer, ^{ NSLog(@"%@",[NSThread currentThread]); }); dispatch_resume(self.timer);Copy the code

Mind you, I want you to puttimerSet to a global variable to be useful.5. Fast iterative method:dispatch_apply

Dispatch_apply adds tasks to a specified queue based on the specified number of times and waits for all queues to complete. Whether on a serial queue or an asynchronous queue, dispatch_apply waits for all tasks to complete, similar to synchronous execution.

    NSLog(@"%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("serial.queue", DISPATCH_QUEUE_SERIAL);
    dispatch_apply(3, queue, ^(size_t index) {
        NSLog(@"dispatch_apply--%ld--%@",index, [NSThread currentThread]);
    });
    NSLog(@"%@",[NSThread currentThread]);
Copy the code

6. Monitoring task method:dispatch_group_notify

NSLog(@"0---%@",[NSThread currentThread]); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"1---%@",[NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"2---%@",[NSThread currentThread]); }); ^{dispatch_group_notify(dispatch_get_main_queue(), dispatch_get_main_queue(), ^{ NSLog(@"3-- %@",[NSThread currentThread]); });Copy the code

7. Blocking thread:dispatch_group_wait

Blocks the current thread and waits for tasks in the group to complete before continuing to execute tasks.

    NSLog(@"0---%@",[NSThread currentThread]);
    dispatch_group_t group =  dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"0---%@",[NSThread currentThread]);
Copy the code

8. dispatch_group_enter,dispatch_group_leave

“Dispatch_group_enter” indicates that a task is added to the specified group. “dispatch_group_leave” indicates that a task is removed from the specified group.

    NSLog(@"%@",[NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"2---%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"3---%@",[NSThread currentThread]);
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"%@",[NSThread currentThread]);
Copy the code

First two asynchronous tasks are added in sequencegroup,dispatch_group_notifyThe function will waitgroupThe task will not be executed until it is completed, anddispatch_group_waitThe function blocks the current thread, etcdispatch_group_notifyFunction until all the tasks above are completed, the next step is executed.9. Semaphore:dispatch_semaphore_t

A semaphore is a synchronization lock used to control the maximum number of concurrent tasks and to convert asynchronous to synchronous execution tasks.

NSLog(@"-----%@",[NSThread currentThread]); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); for (int i = 0; i < 5; I++) {dispatch_async (queue, ^ {NSLog (@ "semaphore: % d -- -- -- -- -- - % @", I, [NSThread currentThread]); dispatch_semaphore_signal(semaphore); }); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); } NSLog(@"-----%@",[NSThread currentThread]);Copy the code
  1. dispatch_semaphore_create(): Creates a semaphore
  2. dispatch_semaphore_wait(): Wait for semaphore, call once, semaphore decrement1. When a semaphore< 0Will block the current thread, according to the incoming time parameter to determine the next task execution, if the time parameter is permanent wait, will wait until there is a signal to continue to execute.
  3. dispatch_semaphore_signal(): release semaphore, call once, semaphore plus1. When a semaphore> = 0Will performdispatch_semaphore_waitThe code after that.

NSOperation

NSOperation is a gCD-based multi-threaded solution provided by Apple to developers. NSOperation is fully object-oriented and is easier to use and more readable than GCD.

How NSOperation implements multithreading:

  1. Create a task: Encapsulate the task to be executedNSOperationIn the object.
  2. Create queue: Create a queueNSOperationQueue.NSOperationNeed to cooperateNSOperationQueueTo achieve multithreading.
  3. Add a task to the queue: willNSOperationObject added toNSOperationQueueIn the.

It is important to note that NSOperation is an abstract class and cannot be used to encapsulate tasks. In practice, you need to use subclasses to encapsulate tasks. There are three ways to use subclasses:

  1. Use a subclassNSInvocationOperation
  2. Use a subclassNSBlockOperation
  3. Custom inherits fromNSOperationSubclass, encapsulating tasks by implementing internal corresponding methods.

NSInvocationOperation

NSLog(@"-----%@",[NSThread currentThread]); NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:nil]; // call the start method to start the task [op start];Copy the code

The task will be executed in the current thread and no new thread will be started.

NSBlockOperation

    NSLog(@"-----%@",[NSThread currentThread]);
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperation----1---%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"NSBlockOperation----2---%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"NSBlockOperation----3---%@", [NSThread currentThread]);
    }];
    [op start];
Copy the code

Put the task directly intoNSBlockOperationtheblockTo execute, is directly executed in the current thread, does not open a new thread, but throughaddExecutionBlock:Method to add a task toNSBlockOperationWhen, a new thread is started to executeaddExecutionBlock:Added tasks.

Custom NSOperation

Define a subclass that inherits from NSOperation, then override its main method, and then use that subclass to perform related tasks.

#import "ZHOperation. H "@implementation ZHOperation - (void)main{NSLog(@"NSOperation: %@",[NSThread currentThread]); } @end NSLog(@"-----%@",[NSThread currentThread]); ZHOperation *op = [[ZHOperation alloc] init]; [op start];Copy the code

Tasks are executed on the main thread and no new thread is opened.

Thread queue NSOperationQueue

NSOperationQueue has two types of queues: primary queue and non-primary queue. Tasks in the primary queue are executed in the main thread, and tasks in the non-primary queue are executed concurrently by default and multithreading is enabled to execute the task.

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init];Copy the code

1. Add the task to the queue

    NSLog(@"-----%@",[NSThread currentThread]);
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *invocationOp1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationAction) object:nil];
    NSInvocationOperation *invocationOp2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationAction) object:nil];
    
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperation---%@", [NSThread currentThread]);
    }];
    [blockOp addExecutionBlock:^{
        NSLog(@"NSBlockOperation---%@", [NSThread currentThread]);
    }];
    [queue addOperation:invocationOp1];
    [queue addOperation:invocationOp2];
    [queue addOperation:blockOp];
    [queue addOperationWithBlock:^{
        NSLog(@"Queue Operation---%@", [NSThread currentThread]);
    }];
Copy the code

The task is executed in a child thread and a new thread is opened to execute the task.

2. The maximum number of concurrent maxConcurrentOperationCount maximum concurrency maxConcurrentOperationCount NSOperationQueue is used to control the serial implementation, concurrent execution.

  • maxConcurrentOperationCountThe default value is- 1, indicates that concurrent execution can be performed without restriction.
  • maxConcurrentOperationCountfor1, the queue is a serial queue, and the tasks are executed sequentially.
  • maxConcurrentOperationCountIs greater than1, the queue is a concurrent queue, and tasks are executed concurrently.
NSLog(@"-----%@",[NSThread currentThread]); NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 2; for (int i = 0; i < 6; I ++) {[queue addOperationWithBlock:^{// a task [NSThread sleepForTimeInterval:3]; NSLog(@" Max -- task %d----%@", I,[NSThread currentThread]); }]; }Copy the code

3. Add task dependency addDependency

1. AddDependency :(NSOperation*)op, add dependencies so that the current task depends on the completion of the task op.

2. RemoveDependency :(NSOperation*)op, removeDependency of current task on op task.

3. Property (readonly, copy) NSArray

dependencies

    NSLog(@"-----%@",[NSThread currentThread]);
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *blockOp1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOp1-----%@",[NSThread currentThread]);
    }];
    NSBlockOperation *blockOp2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOp2-----%@",[NSThread currentThread]);
    }];
    NSBlockOperation *blockOp3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOp3-----%@",[NSThread currentThread]);
    }];
    [blockOp1 addDependency:blockOp2];
    [blockOp2 addDependency:blockOp3];
    
    [queue addOperation:blockOp1];
    [queue addOperation:blockOp2];
    [queue addOperation:blockOp3];
Copy the code

4. Communication between threads

NSLog(@"-----%@",[NSThread currentThread]); NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { NSLog(@"otherqueue---%@", [NSOperationQueue mainQueue] addOperationWithBlock:^{NSLog(@" mainQueue --%@", [NSThread currentThread]); }]; }];Copy the code

5. Perform other operations of NSOperation

1. Cancel the operation

// Cancel a NSOperation [operation cancel]; NSOperation [queue cancelAllOperations] cancels all remaining nsOperations of a NSOperationQueue;Copy the code

2. Queue Pauses and resumes

// Suspend all operation queue.suspended = true; // Restore all operation queue.suspended = false;Copy the code

Pause and cancel actions do not work on tasks that are currently running.

3. Monitor the completion of the task

    NSLog(@"-----%@",[NSThread currentThread]);
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *blockOp1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOp1-----%@",[NSThread currentThread]);
    }];
    [queue addOperation:blockOp1];
    blockOp1.completionBlock = ^{
        NSLog(@"completionBlock-----%@",[NSThread currentThread]);
    };
Copy the code

4. Determine the status of the task

// Whether @property (readonly, getter=isCancelled) BOOL cancelled; @property (readonly, getter=isExecuting) BOOL executing; @Property (readonly, getter=isFinished) BOOL FINISHED;Copy the code