A list,

The NSOperation class was introduced in iOS2.0, implemented by NSThread, but it’s not very efficient. When GCD came out of OS X10.6 and iOS4, NSOperation and NSOperationQueue were rewritten, NSOperation and NSOperationQueue were corresponding to GCD tasks and queues, respectively. So NSOPeration and NSOperationQueue are based on a higher level of encapsulation of the GCD and are completely object-oriented. But it is easier to use than GCD and the code is more readable. Comparing NSOperation to NSOperationQueue brings a little extra overhead to the GCD, but you can add attachments to multiple Operation operations.

Second, knowledge summary

From the NSOperation mind map to understand the overall knowledge related to this class:

NSOperation and NSOperationQueue are higher level encapsulation based on GCD, corresponding to GCD task and queue respectively, completely object-oriented. You can use the start method to directly start the NSOperation subclass object, and the task is executed synchronously by default. Add the NSOperation subclass object to the NSOperationQueue, which defaults to concurrent scheduling tasks.

There are two ways to start an operation. One is to start the operation directly by using the start method, which is executed synchronously by default. The other is to add the operation to the NSOperationQueue, and then the system obtains the operation from the queue and adds it to a new thread for execution, which is executed concurrently by default.

The specific implementation is as follows:

Method 1: Directly start the NSOperation subclass object. The operation that needs to be performed is first encapsulated into an NSOperation subclass object, which then calls the Start method.

Mode 2: When added to the NSOperationQueue object, the queue object starts the operation.

  1. Encapsulate the operations that need to be performed into an NSOperation subclass object
  2. Add this object to the NSOperationQueue
  3. The system extracts the NSOperation subclass object from the NSOperationQueue
  4. Put the fetched operation into a new thread for execution

Using queues to perform operations is divided into two phases: Phase 1: the process of adding to a thread queue, as shown in steps 1 and 2 above. Phase 2: The system automatically takes the thread from the queue and automatically puts it in the thread for execution, as shown in steps 3 and 4 above.

The following is a summary of relevant content:

1. NSOperation

NSOperation is a task-related abstract class that does not have the ability to encapsulate operations and must be subclassed. There are three ways to use the NSOperation subclass:

  • System implementation concrete subclass: NSInvocationOperation
  • System implementation concrete subclass: NSBlockOperation
  • This class is thread-safe and does not have to manage thread life cycle and synchronization issues.

A. NSInvocationOperation subclasses

NSInvocationOperation is a subclass of NSOperation. There are two kinds of ways, create operation object using initWithTarget: selector: object: create sel parameter is the operation of one or zero. Use the initWithInvocation: method to add sel arguments with zero or more operation objects. When the operation object is not added to the queue, no thread is created during the creation of the operation object, and the synchronization operation is performed in the current thread. After the creation is complete, the start method is called directly to start the operation object to perform the operation, or to add the operation object to the NSOperationQueue. A task is added regardless of which method of the subclass is being initialized. Unlike the NSBlockOperation subclass, objects created using NSInvocationOperation will only have one task, since there is no way to add additional tasks.

By default, calling the start method does not create a new thread to perform the operation, but instead synchronously executes the task on the current thread. The operation is executed asynchronously only if it is placed in an NSOperationQueue

B. NSBlockOperation subclasses

You can create an NSBlockOperation object with blockOperationWithBlock: and add a task when you create it. If you want to add more tasks, you can use the addExecutionBlock: method. You can also create an NSBlockOperation object with init:. However, this method does not add tasks when the object is created. You can also add tasks using the addExecutionBlock: method. As with the NSInvocationOperation class, the start operation can be performed by calling the start method and adding to the NSOperationQueue.

The synchronous and asynchronous execution of tasks can be summarized as follows:

  1. The value is used when the number of tasks is 1blockOperationWithBlock:Methods orinit:withaddExecutionBlock:When a single task is created by combining the two methods, no new thread is created and the task is executed on the current thread synchronously.
  2. If the number of tasks is greater than one, useblockOperationWithBlock:Methods orinit:withaddExecutionBlock:Task A is created by combining the two methods. The task is executed synchronously on the current thread without any thread. The NSBlockOperation object usesaddExecutionBlock:Method opens up new threads that execute tasks asynchronously.
  3. Putting an operation into an NSOperationQueue will execute the operation task asynchronously.

Note: You cannot append a task to a block in the completionBlock property, because you cannot add a block task after the operation has started or finished.

C. Custom subclasses

General NSInvocationOperation, NSBlockOperation can meet the needs of use, of course, you can also customize the subclass.

Subclasses need to be created to allow for different cases that might be added to serial and concurrent queues, and different methods need to be overridden. For serial operations, you simply need to rework the main method and add the desired functionality to the method. For concurrent operations, override four methods: Start, Asynchronous, Executing, finished. You also need to create your own automatic release pool because asynchronous operations cannot access the automatic release pool for the main thread.

Note: In custom subclasses, the cancelled property is often used to check if the method is cancelled and to respond to cancellations.

2. NSOperationQueue

It is convenient to manage the operation object by adding the NSOperation object to the NSOperationQueue. Because when we add an operation object to an NSOperationQueue object, the NSOperationQueue object takes the operation from the thread and the work assigned to the corresponding thread is handled by the system.

Whenever a queue is created, operations in the queue are performed in the child thread and are concurrently operated by default. The operations added to the sub-queue NSOperationQueue instance are executed asynchronously

A. Add the operation object to NSOperationQueue

There are three ways to add.

  • addOperation:Add an action
  • addOperationWithBlock:, the system automatically encapsulates an NSBlockOperation object and adds it to the queue
  • addOperations:waitUntilFinished:After an operation object is added to the NSOperationQueue, it usually runs in a short period of time. However, you may need to wait if there are dependencies, or if the entire queue is suspended, etc.

After the operation object is added to the NSOperationQueue, do not modify the status of the operation object. Because an action object can run at any time, changing its dependencies or data can cause unpredictable problems. You can only view the status of the operation object, such as whether the operation object is running, waiting to run, or completed.

B. Set the maximum number of concurrent requests

Although the NSOperationQueue class is designed to execute operations concurrently, it is also possible to force a single queue to schedule only one operation object at a time. SetMaxConcurrentOperationCount: method can set maximum number of concurrent operation of the queue. When set to 1, an NSOperationQueue instance can execute only one NSOperation subclass object at a time. However, the order in which the operation is executed depends on other factors, such as the readiness of the operation and the priority of the operation. So the serialized Operation Queue is not the same as the serial Dispatch Queue in the GCD.

MaxConcurrentOperationCount default is 1, is set to 0. If the maximum number of concurrent requests is not set, the number of concurrent requests is determined by system memory and CPU.

Related concepts:

  1. Concurrency: indicates the number of NSOperation objects in the NSOperationQueue that can be scheduled at the same time.
  2. Maximum concurrency: Specifies the maximum number of NSOperation objects that can be scheduled at a time.

C. Progress modification

When the execution of an action is not complete, we may want to pause the task and then want to continue after some action has been done. To meet this need, Apple has provided us with a suspended property. CancelAllOperations cancelAllOperations cancelAllOperations cancelAllOperations cancelAllOperations cancelAllOperations cancelAllOperations cancelAllOperations cancelAllOperations cancelAllOperations cancelAllOperations cancelAllOperations cancelAllOperations The details are as follows:

For suspended operations, when the NSOperationQueue object property suspended is set to YES, the queue stops scheduling the task. Affects operations that are still in the thread. If the task is executing it will not be affected because the task has already been queued to a thread and executed.

For continuation, the thread operation will continue if the property suspended is set to NO. The queue actively initiates the operations in the queue that are ready to be performed.

Once an NSOperation subclass operation object is added to an NSOperationQueue object, the queue owns the operation object and cannot delete the operation object. If you do not want to execute the operation object, you must cancel the operation object. Cancellation operation can be divided into two cases, cancel an operation and cancel all operations of a queue two cases. Cancel a single operation object by calling the cancel method on an instance of the NSOperation class. CancelAllOperations call the cancelAllOperations method of the NSOperationQueue class instance to cancel all operation objects in the queue.

An operation in a queue can be removed from the queue only if the operation is marked as finished. For unscheduled operations in the queue, the start method is called to perform the operation so that the operation object can handle the cancellation event. Then mark these action objects as finished. For an action object that is executing its task in the thread, the executing task continues and the action object is marked as finished.

Note: Only the operation objects in the scheduling queue will be stopped. The tasks in progress will still be executed, and the cancellation is unrecoverable.

D. role

The NSOperation object can call the start method to execute a task, but by default it is executed synchronously (asynchronous operations can be created, and when NSBlockOperation adds an operand greater than 1, all tasks except the first task are executed asynchronously). If you add an NSOperation to an NSOperationQueue, the operation is then managed by the system, which takes the operation out of the queue and then executes it asynchronously in a new thread. Conclusion: Adding an operation to NSOperationQueue automatically executes the operation and automatically starts the thread

F. Obtain the queue

The system provides two to get the current queue and the main queue. You can get the currentQueue from the class property currentQueue. The mainQueue can be obtained from the class property mainQueue.

3. Rely on

Action objects can add and remove dependencies. When an operation object adds a dependency, the dependent operation object is executed first, and the current operation object is executed only when the dependent operation object is finished. Operation objects added to different thread objects can still rely on each other unilaterally. Remember circular dependencies. This creates an infinite loop.

You can add one or more dependent objects using the addDependency method. eg:[A addDependency:B];

Operation A depends on operation B. Operands manage their own dependencies, so operands in different queues can establish dependencies. However, you must set the dependencies before adding the thread object NSOperationQueue. ** Set dependencies to ensure the order of execution. The order in which an operation is added to a queue does not determine the order of execution. The order of execution depends on various factors such as dependencies, priority, etc.

Call removeDependency: method to remove dependencies.

As shown in the figure, the direction of the arrow is the dependent object. It can be seen from the figure that A depends on B, while B depends on C. So the order of execution is C–> B –>A

4. Thread safety

The NSOperation instance is safe to execute on multiple threads, and no additional locks are required

5. Cancel method

Operations that are in progress will be executed even after receiving the Cancel message.

When the cancel method of an operation in an operation queue is called and the operation queue has pending dependent operations, those dependent operations are ignored. Since the operation has been cancelled, this behavior allows the queue to call the start method of the operation in order to remove the operation from the queue without calling its main method. If the cancel method is called on an operation that is not in the queue, the operation is immediately marked as cancelled.

6. State attributes

A thread has several states, such as uncreated, ready, running, blocked, and dead. An operation object can have multiple states: Executing, Finished, ready. These three properties are provided by Apple to observe the state of the operation object. Because these three properties KVC is compatible with KVO, it is possible to listen for the operation object state properties.

7. The operation is complete

When we need to add some functionality to an operation object after it has completed, we can use the attribute completionBlock to add additional content.

Operation.com pletionBlock = ^ {/ / finish after the operation, can add the content of the};Copy the code

B. Wait until the operation is complete

There are two ways to do this: one is to wait for a single operation object, and the other is to wait for all operations in the queue.

If you want to wait for the entire queue, you can wait for all operations in a queue at the same time. Use of NSOperationQueue waitUntilAllOperationsAreFinished method. While waiting for a queue, other threads of the application can still add operations to the queue, thus potentially increasing the wait time of the thread.

/ / block the current thread, waiting queue all operation is performed [queue waitUntilAllOperationsAreFinished];Copy the code

For best performance on a single operation object, design asynchronous operations whenever possible so that the application can handle other things while the operation is being performed. If you want the result of the operation object processed by the current thread, use the waitUntilFinished method of NSOperation to block the current thread and wait for the operation to complete. This should generally be avoided. Blocking the current thread may be an easy solution, but it introduces more serial code, limits the concurrency of the overall application, and reduces the user experience. Never wait for an Operation in the main application thread. Only wait in a non-operation. Because blocking the main thread will cause the application to fail to respond to user events, the application will also appear unresponsive.

// Block the current thread until an operation completes [Operation waitUntilFinished];Copy the code

8. Execution sequence

The order in which an operation object is added to the NSOperationQueue depends on two points: 1. First, whether the operation object is ready is determined by the dependency of the object. Secondly, it is determined by the relative priority of all the operation objects. The priority level is a property of the operation object itself. All action objects have a “normal” priority by default, but the qualityOfService: method can be used to raise or lower the priority of an action object. Priorities can only be applied to operation objects in the same queue. If the application has multiple operation queues, the priority level of each queue is independent of each other. Therefore, low-priority operations in different queues can still be executed earlier than high-priority operations.

For priority, we can use the queuePriority property to set the highest priority task for an operation object. The higher priority task is, the more likely it will be called, and the order of execution is not guaranteed. And priority does not replace dependencies. Priority only determines the order of execution of the operation objects that have been prepared. The dependencies are satisfied, and then the highest priority of all the prepared operations is selected for execution based on their priority.

9. Service quality

Creates a system priority for an operation based on CPU, network, and disk allocations. A high quality service means that more resources are available to complete the operation faster. It involves priorities for CPU scheduling, I/O priorities, the threads on which tasks run, the order in which they run, and so on

Set the qualityOfService by setting the qualityOfService property. QoS have five priorities, NSQualityOfServiceDefault on by default. Its emergence unifies the priority of all multithreaded technologies in Cocoa. Previously, NSOperation and NSThread were given priority by threadPriority, while GCD was given priority by the number of integers defined by macros such as DISPATCH_QUEUE_PRIORITY_DEFAULT. The correct use of new QoS to specify the priority of threads or tasks enables iOS to allocate hardware resources more intelligently, so as to improve execution efficiency and control power.

Iii. Introduction of related classes

NSOperation

NSOperation is a subclass of NSObject, which represents a single unit of work. It is a task-related abstract class that provides a useful, thread-safe structure for state, priority, dependencies, and management.

There is no point in creating a custom NSOperation subclass; Foundation provides specific implementation subclasses: NSBlockOperation and NSInvocationOperation.

Examples of tasks suitable for NSOperation include network requests, image adjustments, text processing, or any other repeatable, structured, long-running task that generates associated state or data.

An overview

Because the NSOperation class is an abstract class and does not have the ability to encapsulate operations, it cannot be used directly. Instead, subclasses should be used to perform the actual task. There are two types of subclasses: system-defined subclasses (NSInvocationOperation or NSBlockOperation) and user-defined subclasses. Although the NSOperation class is abstract, the basic implementation of the class includes important logic for performing tasks safely. The presence of this built-in logic allows you to focus on the actual implementation of the task, rather than on writing the glue code that will ensure it works properly with other system objects.

An action object is a singleton, that is, once it has performed its task, it cannot do it again. Operations are typically performed by adding them to an operation queue (an instance of the NSOperationQueue class). The operation queue performs its operations either directly by having them run on a helper thread (not the main thread) or indirectly using the LibDispatch library (also known as GCD).

If you do not want to use an action queue, you can call the start method to perform an action directly. Performing operations manually adds more code burden, because opening operations that are not in the ready state throws an exception. The ready property indicates the ready state of the operation.

Operation depends on

Dependencies are a convenient way to execute actions in a particular order. You can add and remove dependencies to an action using the addDependency: and removeDependency: methods. By default, an action object with dependencies is not considered ready until all of its dependencies have been executed. Once the last dependent operation is complete, the operation object becomes ready and ready to execute.

NSOperation supports dependencies that do not distinguish between successful and failed completion of its operations. (In other words, cancellations are also considered completed.) It is up to you to decide whether a dependent operation should continue if its dependent operation is cancelled or does not complete the task successfully. This may require the incorporation of some additional error tracking capabilities into the action object.

Compatible with KVO attributes

The NSOperation class is KVC and KVO compatible for some of its properties. You can look at these properties to control other parts of the application as needed. Use the following key path to view properties:

  • IsCancelled – read-only
  • IsAsynchronous – read-only
  • IsExecuting – read-only
  • IsFinished – read-only
  • IsReady to read-only
  • Dependencies – read-only
  • Read and write queuePriority –
  • Read and write completionBlock –

While you can add observers to these attributes, you should not use Cocoa Bindings to bind them to user interface related elements. User interface-related code is usually executed only in the main thread of the application. Because an operation can be performed on any thread, the operation KVO notification can also occur on any thread.

If you provide a custom implementation for the previous property, the implementation content must remain compatible with BOTH KVC and KVO. If you define additional properties for the NSOperation object, it is recommended that you also keep these properties KVC and KVO compatible.

Multi-core considerations

Methods on the NSOperation object can be safely called from multiple threads without the need to create additional locks to synchronize access to the object. This behavior is necessary because an operation is usually created and monitored on a separate thread.

When subclassing NSOperation, you must ensure that any overridden method can be safely called from multiple threads. If you implement custom methods in subclasses, such as custom data accessors (getters), you must ensure that these methods are thread-safe. Therefore, access to any data variable must be synchronized to prevent potential data corruption. For more information on information synchronization, see the Threading Programming Guide.

Asynchronous VS synchronous operations

If you want to manually execute an operation object rather than adding it to a queue, you can design both synchronous and asynchronous ways to perform the operation. Operation objects are synchronous by default. In a synchronous operation, the action object does not create a separate thread to run its tasks. When the start method of a synchronous operation is called directly, the operation is executed immediately in the current thread. The task is completed when the object’s start method is returned to the caller.

When you call the start method of an asynchronous operation, the method may return before the task is complete. An asynchronous operation object is responsible for scheduling tasks on a separate thread. This is done by directly starting a new thread, calling an asynchronous method, or submitting a block to the dispatch queue. An asynchronous manipulation object can directly start a new thread. (Has the ability to create new threads, but is not necessarily good to start new threads, because CPU resources are limited, it is impossible to open an infinite number of threads)

If you use queues to perform operations, it is very simple to define them as synchronous operations. If you perform the operation manually, you can define the operation object as asynchronous. Defining an asynchronous operation is more work because you have to monitor the status of ongoing tasks and notify status changes using reported KVO. But defining asynchronous operations is especially useful in cases where you want to make sure that performing the operations manually does not block the calling thread.

When adding an action to an action queue, the action in the queue ignores the value of the asynchronous property and always calls the start method from a separate thread. Therefore, if you always run operations by adding them to the operation queue, there is no reason to make them asynchronous.

Subclass comments

The NSOperation class provides the basic logic to track the execution state of an operation, but it must be subclassed to do the actual work. How a subclass is created depends on whether the subclass is designed to be concurrent or non-concurrent.

Method overloading

For non-concurrent operations, only one method is usually overridden

  • main

In this method, you need to add the necessary code to perform a specific task. Of course, you can also define a custom initialization method to make it easier to create instances of your custom class. You may also want to define getter and setter methods to access data from operations. However, if you define custom getter and setter methods, you must ensure that it is safe for those methods to be called from multiple threads.

If you create a concurrent operation, you need to override at least the following methods and properties:

  • start
  • asynchronous
  • executing
  • finished

In concurrent operations, the start method is responsible for starting the operation asynchronously. This method determines whether to generate a thread or call an asynchronous function. The start method should also update the execution status of the Operation Executing property as a report before the operation is started. This can be done by sending the KVO notification of the key path to interested clients to let them know that the operation is running. The Executing property must also provide state in a thread-safe manner.

When a task is about to complete or cancel, the concurrent operation object must generate the KVO notification of isExecuting and isFinished keys to mark the final changed state of the operation. In the case of cancellation, it is still important to update the isFinished key path, even if the operation does not fully complete its task. Queued operations must be reported before the queued deletion operation. In addition to generating the KVO notification, overwrites of the Executing and Finished properties should continue reporting the exact value of the operation’s status.

Important: In the start method, super should never be called. When defining a concurrent operation, you need to provide yourself the same behavior as the default start method, including starting tasks and generating the appropriate KVO notifications. The start method should also check whether the operation itself is canceled before actually starting the task.

For concurrent operations, there should be no need to override methods other than those described above. However, if you customize the dependent features of the operation, you may have to override additional methods and provide additional KVO notifications. For dependencies, this may be as simple as providing notification of the isReady key path. Because the Dependencies attribute contains a list of dependent actions, changes to it are already handled by the default NSOperation class.

Maintain the status of the operation object

An action object maintains state information about the content to determine when it is safe to execute and to notify outsiders of its task progress during the lifecycle of the operation. Custom subclasses need to maintain state information to ensure that the actions performed in the code are correct. The key paths associated with the operation state are:

  • isReady

    The key path lets the client know when an operation is ready to execute. This property is true if the operation is ready to execute immediately, and false if any of its dependencies are incomplete. In most cases, it is not necessary to manage the state of this key path yourself. If the ready state of an operation is determined by an operation dependent factor (such as some external condition in your program), then you can provide an implementation of the ready property and track the ready state of the operation. Although it is often simpler to create action objects only when the external state allows it.

    In macOS 10.6 or later, if the canceled operation is waiting for one or more dependent operations to complete, those dependencies will be ignored and the value of this property will be updated to be ready to run. This behavior gives the operation queue an opportunity to clear cancelled operations from the queue more quickly.

  • isExecuting

    The key path lets the client know whether the operation is performing its assigned task. True if the operation is processing its task; Otherwise, the value is false.

    If you replace the start method of the operation object, you must also replace the Executing property and generate a KVO notification if the execution state of the operation changes.

  • isFinished

    The key path lets the client know that the operation successfully completed the task or was canceled and exits. The dependency is not cleared until the isFinished key path value is true. Similarly, an operation queue does not exit the operation queue until the finished attribute is true. Therefore, marking operations as completed is important to prevent queued backups of ongoing or canceled operations.

    If you replace the start method of the operation object, you must also replace the Executing property and generate a KVO notification if the execution state of the operation changes.

  • isCancelled

    The isCancelled key path lets the client know that an operation is being cancelled. Voluntary cancellations are supported, but unsolicited KVO notifications for this key path are discouraged.

Responding to cancel command

Once an action is added to the queue, it is out of your control. The queue takes over and schedules the task. However, if you ultimately decide that you don’t want to do something, such as when the user presses the cancel button or exits the application, you can cancel the action to prevent unnecessary CPU time consumption. You can do this by calling the Cancel method of the operation object itself or by calling the cancelAllOperations method of the NSOperationQueue class.

Canceling an operation does not immediately force it to stop doing what it is doing. Although all operations need to consider the value in the cancelled property, they must explicitly check the value in that property and abort as needed. The default implementation of NSOperation includes canceling checks. For example, if an operation is cancelled before its start method is called, the start method exits without starting the task.

prompt

In macOS 10.6 or later, if the cancel method of an operation in an operation queue is called and the operation queue has pending dependent operations, those dependent operations are subsequently ignored. Since the operation has been cancelled, this behavior allows the queue to call the start method of the operation in order to remove the operation from the queue without calling its main method. If the cancel method is called on an operation that is not in the queue, the operation is immediately marked as cancelled. In each case, the appropriate KVO notification is generated when the action is marked ready or completed.

Cancellation semantics should always be supported in any custom code you write. In particular, the main task code should check the value of the cancelled property periodically. If the property value is YES, the operation object should be cleaned up and exit as soon as possible. If you implement a custom start method, the method should include early cancellation checks and execute appropriately. Your custom start method must be prepared to handle this type of early cancellation.

In addition to simply exiting when an action is canceled, it is also important to move the canceled action to the appropriate final state. Specifically, if you manage the finished and Executing properties yourself (probably because you are implementing concurrent operations), then you must update the properties accordingly. In this case, you must change the value returned by finished to YES and in executing to NO. You must make these changes even if the action is cancelled before execution can begin.

Properties and Methods

Initialize the

// Return an initialized NSOperation object - (instanceType)init; // Superclass NSObject methodCopy the code

Perform operations

// Start the main method task of NSOperation if the current status and dependencies of the task are appropriate. Note that the default implementation only runs in the current thread. If concurrent execution is required, the subclass must override this method and make the attribute Asynchronous return YES. - (void)start; // Execute the non-concurrent tasks of the receiver (NSOperation). The entry to the operation task, usually used to customize the NSOperation subclass - (void)main; // Execute this block after the main operation is complete // Since NSOperation may be cancelled, So the code that runs in the block should be independent of the core task of NSOperation @ Property (nullable, copy) void (^completionBlock)(void);Copy the code

Cancel the operation

// Notifies the NSOperation object to stop executing its task. Mark the state isCancelled. // the operation will not be cancelled immediately after the call is called. You need to check whether the operation isCancelled by isCancelled method, and then write your own code to exit the current operation - (void)cancel;Copy the code

Get operational status

@property (readonly, getter=isCancelled) BOOL cancelled; @property (readonly, getter=isExecuting) BOOL Executing @property (readonly, getter=isExecuting) BOOL; // Boolean indicating whether the operation is completing. @Property (readonly, getter=isFinished) BOOL Finished; @property (readonly, getter=isAsynchronous) BOOL asynchronous; // Boolean, indicating whether the operation can be executed immediately (ready state) @property (readonly, getter=isReady) BOOL Ready; @property (nullable, copy) NSString *name;Copy the code

Management depends on

// Add a dependency to make the sink dependent on the specified completion operation. [op1 addDependency:op2]; - (void)addDependency:(NSOperation *)op; - (void)addDependency:(NSOperation *)op; - (void)removeDependency:(NSOperation *)op (NSOperation *)op (NSOperation *)op; // An array of operation objects that must be executed before the current object is executed. @property (readonly, copy) NSArray<NSOperation *> *dependencies;Copy the code

Execution priority

// Operate on the relative importance of obtaining system resources. Property NSQualityOfService qualityOfService;Copy the code

Wait for an action object

// Block the execution of the current thread until the operation object completes its task. Can be used for synchronization of the execution order of threads. - (void)waitUntilFinished;Copy the code

constant

// These constants allow you to prioritize the order in which operations are performed. NSOperationQueuePriority // is used to indicate the nature and importance of the work to the system. Higher-quality classes get more resources than lower-quality classes. NSQualityOfServiceCopy the code
/ / NSOperation priority enumeration typedef NS_ENUM (NSInteger NSOperationQueuePriority) {NSOperationQueuePriorityVeryLow = 8 l, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8 };Copy the code

Since iOS8, apple has provided several Quality of Service enumerations to use: user interactive, user initiated, utility, and background. These enumerations tell the system what kind of work we are doing. The system then executes the task code in the most efficient way with reasonable resource control, such as CPU scheduling priority, I/O priority, which thread the task is running on, and the order in which it is running. We can use an abstract Quality of Service enumeration parameter to indicate the intent and category of the task

// Tasks that interact with the user, often related to UI level refreshes, such as animations, should be completed in a flash. NSQualityOfServiceUserInteractive / / initiated by the user's task and need immediate results, such as sliding scroll view to load the data for subsequent cell shows that these tasks are usually associated with subsequent user interaction, In a few seconds, or a shorter period of time to complete the NSQualityOfServiceUserInitiated / / some tasks may need to spend some time, these tasks do not need to return results immediately, such as download task, These tasks may take several seconds or several minutes NSQualityOfServiceUtility / / some tasks may need to spend some time, these tasks do not need to return results immediately, such as download task, These tasks may take several seconds or several minutes NSQualityOfServiceBackground / / some tasks may need to spend some time, these tasks do not need to return results immediately, such as download task, These tasks may take several seconds or several minutes NSQualityOfServiceDefaultCopy the code

The priority of eg:Utility and below is controlled by low power mode in iOS9. In addition, 90% of tasks should have priority below Utility when no user is involved.

NSBlockOperation

A subclass of NSOperation that manages concurrent operations for one or more blocks.

An overview

The NSBlockOperation class is a concrete subclass of NSOperation that manages the concurrent execution of one or more blocks. You can use this object to execute multiple blocks at once, rather than creating separate action objects for each block. When multiple blocks are executed, the operation itself is considered complete only when all blocks have completed execution.

Blocks added to an operation are assigned to the appropriate work queue at default priority.

The method attributes

Manage blocks in an operation

// Create and return an NSBlockOperation object and add the specified block to it. + (instancetype)blockOperationWithBlock:(void (^)(void))block; // Add the specified block to the list of blocks to execute. - (void)addExecutionBlock:(void (^)(void))block; // The block associated with the receiver. @property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;Copy the code

NSInvocationOperation

A subclass of NSOperation that manages the operations performed as calls to the individual encapsulated tasks specified.

An overview

The NSInvocationOperation class is a concrete subclass of NSOperation that can be used to initialize an operation that involves calling a selector on a specified object. This class implements a non-concurrent operation.

The method attributes

Initialize the

// Returns an NSInvocationOperation object initialized with the specified target and selector. - (instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg; // Returns the NSInvocationOperation object initialized with the specified call object. - (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;Copy the code

Retrieve attributes

// The receiver's call object. @property (readonly, retain) NSInvocation *invocation; @property (nullable, readonly, retain) id result;Copy the code

constant

// The name of the exception raised by NSInvocationOperation if an error occurs when the result method is called. Result ExceptionsCopy the code

NSOperationQueue

Manage the queue in which the operation is executed.

An overview

NSObject subclass. The operation queue executes its queued NSOperation objects according to their priority and readiness. After being added to the operation queue, the operation remains in its queue until it reports the end of its task. You cannot delete an operation directly from a queue after it has been added.

Tip: The operation queue retains operations until they are complete, and the queue itself retains operations until all operations are complete. Suspending an operation queue with an unfinished operation can result in a memory leak.

Determine the order of execution

Operations in a queue are organized according to their state, priority, and dependencies, and are executed accordingly. If all queued operations have the same queuePriority and are ready to be executed when they are placed in the queue (that is, their ready property returns yes), they will be executed in the order they were submitted to the queue. Otherwise, the operation queue always performs the highest priority operation.

However, you should not rely on queue semantics to ensure a particular order of execution for the operations, because a change in the readiness state of the operations could change the final order of execution. Inter-operation dependencies provide an absolute order of execution for operations, even if they are in different operation queues. An action object is not considered ready for execution until all of its dependent operations have completed their execution.

Cancel the operation

Ending a task does not necessarily mean that the operation has completed the task. An operation can also be cancelled. Canceling an action object leaves the object in the queue, but informs the object that it should stop its work as soon as possible. For an operation that is currently performing, this means that the operation object must check the cancellation status, stop the operation it is performing, and mark itself as finished. For operations that are queued but not yet executed, the queue still needs to call the start method of the action object so that it can handle the cancellation event and mark itself as finished.

Prompting to cancel an operation causes the operation to ignore any dependencies it may have. This behavior enables the queue to perform the start method of the operation as soon as possible. The start method, in turn, moves the action to the end state so that it can be removed from the queue.

KVO compatibility attributes

The NSOperationQueue class is compliant with both key-value encoding (KVC) and key-value observation (KVO). These properties can be viewed as needed to control the rest of the application. To view properties, use the following key path:

  • Operations – read-only
  • OperationCount – read-only
  • Read and write maxConcurrentOperationCount –
  • Read and write suspended –
  • Read and write the name –

While you can attach observers to these attributes, you should not use Cocoa Bindings to bind them to the relevant elements of the user interface. Tasks associated with a user interface can normally only be performed in the main thread of an application. However, KVO notifications associated with an operation queue can occur in any thread.

Thread safety

It is safe to use an NSOperationQueue object from multiple threads without creating additional locks to synchronize access to the object.

An operation queue uses a scheduling framework to start the execution of its operations. Therefore, operations are always executed on separate threads, regardless of whether they are specified as synchronous or asynchronous.

Attributes & Methods

Access a specific action queue

// Return the operation queue associated with the main thread. There is always a queue by default. @property (class, readonly, strong) NSOperationQueue *mainQueue; // Return the operation queue that started the current operation. @property (class, readonly, strong, nullable) NSOperationQueue *currentQueue;Copy the code

Manage the operations in the queue

// Adds the specified action to the sink. - (void)addOperation:(NSOperation *)op; // Add the specified operation to the queue. - (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait; // Wrap the specified block in an operation and add it to the sink. - (void)addOperationWithBlock:(void (^)(void))block; // The current operation in the queue. @property (readonly, copy) NSArray<__kindof NSOperation *> *operations; // The current operand in the queue. @property (readonly) NSUInteger operationCount; // Cancel all queued and executed operations. - (void)cancelAllOperations; / / block the current thread until all the receiver's queue and executing operation is finished - (void) waitUntilAllOperationsAreFinished;Copy the code

Manage the execution of operations

// Default service level applied to operations performed using queues. @property NSQualityOfService qualityOfService; // Maximum number of queue operations that can be performed simultaneously. @property NSInteger maxConcurrentOperationCount; // The default maximum number of concurrent operations to be executed in the queue. NSOperationQueueDefaultMaxConcurrentOperationCountCopy the code

suspended

// A Boolean value indicating whether the queue is actively scheduling operations to be performed. @property (getter=isSuspended) BOOL suspended;Copy the code

When the value of this attribute is NO, the queue actively initiates the operations in the queue that are ready to be performed. Setting this property to YES prevents the queue from starting any queued operations, but operations that have already been performed continue. Actions can continue to be added to the pending queue, but they will not be scheduled for execution until this property is changed to NO. The operation is deleted from the queue only after it has finished executing. However, in order to end execution, an action must first be started. Because a pending queue does not start any new operations, it does not delete any operations that are currently queued but not performed (including canceled operations).

You can use key observation to monitor changes in the value of this property. Configure an observer to monitor the path of the suspended key of the operation queue. The default value for this property is NO.

The queue configuration

@property (nullable, copy) NSString *name; // The schedule queue used to perform the operation. @property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue;Copy the code

Four, the use of

1. NSInvocationOperation

Create: Call the Start method to Start. By default, calling the start method does not create a new thread to perform the operation, but instead synchronizes the operation on the current thread.

Creation method 1: Use the initWithInvocation method to set 0 or more parameters

NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:@selector(addSig:)]; NSInvocation *invo = [NSInvocation invocationWithMethodSignature:sig]; NSString * info = @"NSMethodSignature"; [invo setTarget:self]; [invo setSelector:@selector(addSig:)]; //argumentLocation specifies arguments, as Pointers. // idx specifies the index of arguments. The first argument starts with index 2, because index is 1. [invo setArgument:(__bridge void *)(info) atIndex:2]; NSInvocationOperation *invocationOp = [[NSInvocationOperation alloc] initWithInvocation:invo]; [invocationOp start];Copy the code

Creation method 2: Use initWithTarget

// Initialize NSInvocationOperation *invocationOp = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOpSel:) object:@"111"]; // The first of the operations // execute [invocationOp start];Copy the code

2. NSBlockOperation

The first action task is created, typically without opening a new thread, and is executed in the current thread. The next task is to create new threads. Perform asynchronous tasks.

Creation method 1: Use init to create an operation object, and then use addExecutionBlock to add and execute the operation object

NSBlockOperation * op1 = [[NSBlockOperation alloc] init];
 [op1 addExecutionBlock:^{
     NSLog(@"1 beign");
     NSLog(@"1--%@",[NSThread currentThread]);
     NSLog(@"1 end");
 }];
 [op addExecutionBlock:^{
     NSLog(@"2 beign");
     NSLog(@"2--%@,currentQueue >>>> %@",[NSThread currentThread],[NSOperationQueue currentQueue]);
     NSLog(@"2 end");
 }];

 [op addExecutionBlock:^{
     NSLog(@"3 beign");
     NSLog(@"3--%@,currentQueue >>>> %@",[NSThread currentThread],[NSOperationQueue currentQueue]);
     NSLog(@"3 end");
 }];
 [op1 start];
Copy the code

Creation method 2: Use the blockOperationWithBlock command to create an operation object

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

3. NSOperationQueue

3.1. Add the operation object to the queue

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

3.2. Adding a Dependency

It makes no sense to start an operation object directly with start instead of adding the operation object to the NSOperationQueue object. Because when the start message is sent to the operation object, the operation is started, and if the thread is not blocked, the task is executed immediately. So there is no order of execution. After operation objects are added to the NSOperationQueue, tasks can be scheduled in the queue based on factors such as dependency and priority.

Note: Be sure to set the dependencies before adding the thread object NSOperationQueue. Otherwise dependency will not achieve the desired effect.

A. Interqueue dependency

NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // Create a queue. NSInvocationOperation *invocationOp = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOpSel:) object:@"invocationOp--arg"]; NSInvocationOperation *invocationOp2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOp2Sel:) object:@"invocationOp2--arg"]; // Set dependencies. The invocationOp task will be executed only after the invocationOp2 task is completed. [invocationOp addDependency:invocationOp2]; / / perform [queue addOperation: invocationOp]; [queue addOperation:invocationOp2];Copy the code

B. Dependencies between different queues

NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // Create a queue. NSBlockOperation *block1Op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"block1Op -- begin"); [NSThread NSLog(@"block1Op -- end");}]; NSBlockOperation *block2Op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block2Op -- begin"); [NSThread NSLog(@"block2Op -- end");}]; NSOperationQueue *queue2 = [[NSOperationQueue alloc] init]; NSBlockOperation *block3Op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"block3Op -- begin"); [NSThread NSLog(@"block3Op -- end");}]; NSBlockOperation *block4Op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block4Op -- begin"); [NSThread NSLog(@"block4Op -- end");}]; // Set dependencies. The invocationOp task will be executed only after the invocationOp2 task is completed. [block1Op addDependency:block3Op]; [block3Op addDependency:block2Op]; // block2Op --> block3Op --> block1Op // Add an operation to the queue [queue addOperation:block1Op]; [queue addOperation:block2Op]; [queue2 addOperation:block3Op]; [queue2 addOperation:block4Op];Copy the code

From the above code, you can obtain the execution order of block1Op, block2Op, and block3Op operations: block2Op –> block3Op –> block1Op.

NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"blockOp")); // Simulate the time-consuming operation [NSThread sleepForTimeInterval:3]; }]; NSBlockOperation *block2Op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block2Op -- begin"); // [blockOp waitUntilFinished]; NSLog(@"block2Op --end");}]; [blockOp waitUntilFinished]; // Run [queue addOperation:blockOp]; [queue addOperation:block2Op];Copy the code

Get the main queue

NSOperationQueue *queue = [NSOperationQueue mainQueue];
Copy the code

3.4. Get properties Get the current queue

NSOperationQueue *queue = [NSOperationQueue currentQueue];
Copy the code

3.5. Progress modification: Pause, continue, and cancel the NSOperationQueue.

// Initialize queue - (NSOperationQueue *)manualQueue{if (! _manualQueue) { _manualQueue = [NSOperationQueue new]; _manualQueue.maxConcurrentOperationCount = 2; } return _manualQueue; } NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"1--start"); [NSThread sleepForTimeInterval:3]; NSLog(@"1--end"); }]; NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"2--start"); [NSThread sleepForTimeInterval:1]; NSLog(@"2--end"); }]; NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"3--start"); [NSThread sleepForTimeInterval:4]; NSLog(@"3--end"); }]; NSBlockOperation *blockOperation4 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"4--start"); [NSThread sleepForTimeInterval:3]; NSLog(@"4--end"); }]; [self.manualQueue addOperation:blockOperation1]; [self.manualQueue addOperation:blockOperation2]; [self.manualQueue addOperation:blockOperation3]; [self.manualQueue addOperation:blockOperation4];Copy the code

A. stop

If the task is running, it will not be affected. Because the task has been queued to a thread and is scheduled to execute. When the NSOperationQueue object property suspended is set to YES, the queue stops scheduling tasks. Affects operations that are still in the thread.

self.manualQueue.suspended = YES;
Copy the code

B. continue to

The queue actively initiates the operations in the queue that are ready to be performed.

self.manualQueue.suspended = NO;
Copy the code

C. to cancel

An operation in a queue can be removed from the queue only if the operation is marked as finished.

  1. For unscheduled operations in the queue, the start method is called to perform the operation so that the operation object can handle the cancellation event. Then mark these action objects as finished.
  2. For an action object that is executing its task in the thread, the executing task continues and the action object is marked as finished.
[self.manualQueue cancelAllOperations];
Copy the code

3.6. The operation is complete

A. The listening operation is complete

You can add additional content after the action is completed. Using the property completionBlock, you can add additional actions to the NSOperation object when the task is complete. However, you cannot append a task to the completionBlock because you cannot add a block task after an operation has started or finished.

NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{// Added task}]; NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{// Added task}]; BlockOperation1.com pletionBlock = ^ {/ / add additional content}; [blockOperation1 start];Copy the code

B. After the listening operation is complete, a waitUntilFinished: message is sent to an operation object. The current thread will be blocked and the task of the operation object that sent the message has completed. The current thread is aroused, enters the ready state, and begins to execute the corresponding task.

NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // Create a queue. NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:3]; // Simulate time-consuming operation}]; NSBlockOperation *block2Op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block2Op -- begin"); [blockOp NSLog(@"block2Op --end");}]; // Wait until the blockOp task is complete. // Run [queue addOperation:blockOp]; [queue addOperation:block2Op];Copy the code

3.7. Maximum concurrency

NSOperationQueue is concurrent queue, maxConcurrentOperationCount said maximum concurrency. When the maxConcurrentOperationCount is 1, although NSOperationQueue object is the default concurrent scheduling NSOperation object, but in fact, at this point, the NSOperationQueue object is serial queue. However, unlike GCD serialization, dependency and priority factors affect the order in which NSOperationQueue objects schedule tasks. The order in which NSOperation objects are added is not necessarily the order in which they are scheduled.

NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // Create a queue. NSBlockOperation *block1Op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"block1Op -- begin"); [NSThread NSLog(@"block1Op -- end");}]; NSBlockOperation *block2Op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"block2Op -- begin"); [NSThread NSLog(@"block2Op -- end");}]; queue.maxConcurrentOperationCount = 1; [block1Op addDependency:block2Op]; / / add dependent / / block2Op queuePriority = NSOperationQueuePriorityHigh; [queue addOperation:block1Op]; [queue addOperation:block2Op];Copy the code

Customize NSOperation subclasses

Relevant concepts

  1. The serial (non-concurrent) case
  • Common usage scenarios: Related to the Internet, such as image downloading
  • Using the step
    • Implement the init method, which initializes the operation object and some other objects
    • Override the main method and implement the method you want to execute in it
    • In the main method, create the automatic release pool because the automatic release pool for the main thread cannot be accessed if the operation is asynchronous
    • Always check if the method is cancelled by passing the Cancelled property and respond to cancellations
  • Responding to cancellation events
    • Cancellation events can occur at any time
    • The object’s isCancelled method is called periodically, and if it returns “YES”, it returns immediately and no longer executes the task. The isCancelled method itself is very lightweight and can be called frequently without any significant performance penalty
  • Place call
    • Before performing any actual work
    • It is called at least once during each iteration of the loop or more frequently if each iteration is relatively long
    • Any point in code where it is relatively easy to abort an operation
  1. concurrent
  • Overriding methods
    • Four methods must be overwritten: start, asynchronous, Executing, finished
    • Start (required) : All concurrent operations must override this method and need to replace the default behavior with a custom implementation. At no time can the start method of the superclass be called. That is, you cannot use super. The overridden start method is responsible for starting an operation asynchronously, either by starting a thread or calling an asynchronous function. Note that the execution status of the operation should be updated in start before starting the operation, because the execution status of the current operation is sent to the key path of KVO for easy viewing of the operation status.
    • Main (optional) : In this method, place the code needed to perform a given task. You should define a custom initialization method to make it easier to create instances of your custom class. When you define custom getter and setter methods, you must ensure that these methods can be safely called from multiple threads. Although the task can be performed in the start method, implementing the task using this method allows for a clearer separation of setup and task code by calling the mian method in the start method. Note: Define a separate automatic release pool from other threads.
    • IsFinished (required) : indicates whether it has been completed. A KVO notification mechanism needs to be implemented.
    • IsAsynchronous (required) : NO is returned by default, indicating non-concurrent execution. Concurrent execution requires customization and returns YES. The return value is then used to determine whether or not to be concurrent.
    • IsExecuting: Indicates whether a KVO notification mechanism needs to be implemented in executing.

Note: create your own automatic release pool, asynchronous operations cannot access the main thread automatic release pool

use

Here’s an example of how to do this: In the case of non-concurrency you need to override main, and it’s better to add an init method to initialize the data.

+ (instancetype)downloaderOperationWithURLPath:(NSString *)urlPath completeBlock:(CompleteBlock)completeBlock{ WNNoCurrentOPration *op = [[WNNoCurrentOPration alloc] init]; op.urlPath = urlPath; op.completeBlock = completeBlock; return op; } @autoreleasepool {NSLog(@"%s",__func__);} @autoreleasepool {NSLog(@"%s",__func__); If (self.isCancelled) return; if (self.isCancelled) return; / / download pictures of lengthy operations NSURL * url = [NSURL URLWithString: self, urlPath]; NSData *data = [NSData dataWithContentsOfURL:url]; NSLog(@" %@",[NSThread currentThread]); UIImage *image = [UIImage imageWithData:data]; // Call back to the main thread to notify the caller of completion of the callback dispatch_async(dispatch_get_main_queue(), ^{if (self.completeBlock! = nil) { self.completeBlock(image); }}); }}Copy the code

GCD VS NSOperation

GCD is a solution proposed by Apple for multi-core parallel computing, which automatically utilizes more CPU cores (such as dual-core or quad-core), while NSOperation is an object-oriented encapsulation based on GCD, with features of GCD. GCD is a function that adds a task (block) to a queue (serial/parallel/global/main queue) and executes the task synchronously/asynchronously, while NSOperation adds an operation (typically an asynchronous task) to a queue (typically a concurrent queue) and executes a function that specifies the operation.

In contrast to NSThreads or cross-platform PThreads, GCD and NSOperation automatically manage the thread life cycle, so developers do not need to write any thread management code as long as they focus on the specific task logic.

GCD provides some functions that NSOperation does not have: delayed execution, one-time execution, scheduling group; NSOperation provides some convenient operations: maximum concurrency, queue temporary/continue, cancel all operations, specify the dependencies between operations (GCD can be implemented with synchronization);

GCD does not control the maximum number of concurrent threads, while NSOperation can set the maximum number of concurrent threads and can be flexible to limit the number of threads as needed. This is because creating threads consumes the necessary resources.

When to Use GCD: Dispatch queues, Groups, semaphores, and Barriers form a basic set of concurrency primitives. For one-time execution, or simply to speed up existing methods, it is more convenient to use lightweight GCD Dispatch than NSOperation.

When to use NSOperation: NSOperation objects can be scheduled with a series of dependencies under a particular queue priority and quality of service (used to indicate the nature and importance of the work to the system). Unlike blocks scheduled on a GCD queue, NSOperation can be cancelled and its operational state queried. By subclassing, NSOperation can be associated with the execution result for later reference.

Note: NSOperation and GCD are not mutually exclusive.

Queue VS Thread VS task

Get the whole picture from mind mapping.

1. The queue (queue)

Queues are first-in, first-out characteristic data structures. And the queue is only responsible for the scheduling of tasks, not the execution of tasks. According to the scheduling mode of tasks, it can be divided into serial queue and concurrent queue. The characteristics are summarized as follows:

  • Serial queues
    • Schedule tasks one by one
    • No matter whether the execution task specified in the queue is synchronous or asynchronous, it waits for the execution of the previous task to complete before scheduling subsequent tasks.
  • Concurrent queue
    • Multiple tasks can be scheduled simultaneously.
    • If the current scheduled task is executed synchronously, subsequent tasks are scheduled after the task is completed.
    • If the current scheduled task is executed asynchronously and the underlying thread pool has available thread resources, a new thread will be scheduled for subsequent tasks.

We know that the system provides two queues: the main queue and the global concurrent queue. We can also create our own queues.

  • The home side column
    • The characteristics of
      • Tasks added to the main queue are executed in the main thread.
      • A queue dedicated to scheduling tasks on the main thread.
      • Tasks in the queue are scheduled to execute on the main thread only when the main thread is idle.
      • The thread is not started.
      • Serial port.
    • To obtain
      • Is created with the program launch.
      • The main queue only needs to be fetched, not created.
      • The main queue is responsible for scheduling tasks on the main thread.
  • Global queue
    • The essence is a concurrent queue, provided by the system, easy programming, can not be created on the direct use.
    • The global queue is shared by all applications
    • A queue of the GCD
    • Global queues do not have names, but concurrent queues do. A name helps you view system logs
  • Custom queue
    • There are two modes: serial and concurrent.
    • Tasks added to the custom queue are automatically executed in the child thread.

2. Thread (thread)

  • The main cost of creating a thread is: the kernel data structure (about 1KB), the stack space (512KB for child threads and 1MB for main threads), and the creation of a thread takes about 90 milliseconds
  • For a single-core CPU, the CPU can only process one thread at a time, that is, only one thread is executing, and multiple threads are executing concurrently (at the same time). In fact, the CPU rapidly schedules (switches) between multiple threads. If the CPU schedules threads quickly enough, it creates the impression that multiple threads are executing concurrently.
  • Threads are the basic unit of CPU scheduling and dispatch that can operate independently.
  • Threads perform tasks, units of function that actually do things.
  • Asynchronous: New threads are created.

3. The task (task)

Be sure to distinguish between queues, threads, and tasks: the queue schedules the task, adds the task to the corresponding thread, and then executes the task in the thread. Tasks can be executed synchronously or asynchronously.

  • synchronous
    • If the current task is not completed, the next task will not be executed
    • No ability to create new threads
  • asynchronous
    • If the current task is not complete, you can perform the next task
    • The ability to create new threads is available, but not necessarily available. Creating threads requires resources such as CPU, and system resources are limited. It is impossible to create an infinite number of threads.

Recommend the blog

  • NSOperation apple official
  • NSHipster NSOperation