NSOperation

An abstract class that represents code and data associated with a single task.

API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0))
@interface NSOperation : NSObject
Copy the code

Because the NSOperation class is an abstract class, you do not use it directly, but use its subclasses or use one of the system-defined subclasses (NSInvocationOperation or NSBlockOperation) to perform real tasks. Although the basic implementation of NSOperation is abstract, it does contain important logic to coordinate the safe execution of the task. The existence of this built-in logic allows you to focus on the actual implementation of the task, rather than the glue code needed to make sure it works correctly with other system objects.

The operation object is a single-shot object, meaning it only performs the task once and cannot be used to perform it again. Operations are typically performed by adding operations to the operation queue (an instance of the NSOperationQueue class). Action queues perform their operations directly by running operations on child threads, or indirectly by using the LibDispatch library (also known as Grand Central Dispatch). For more information on how queues perform operations, see NSOperationQueue.

If you don’t want to use an action queue, you can call the start method of the operation directly from the code to perform the operation. Performing the operation manually places a greater burden on the code, since starting an operation that is not in the Ready state raises an exception. The ready attribute reports whether the operation is ready.

Operation Dependencies

Dependencies are a convenient way to perform operations in a particular order. You can add and remove dependencies for an operation using the addDependency: and removedDependency: methods. By default, action objects with dependencies are not considered ready until all of their dependent action objects have completed execution. However, once the last dependency operation completes, the operation object is ready to execute.

The dependency supported by NSOperation does not distinguish whether the dependent operation is successfully completed. (In other words, canceling an action also marks it as completed.) If a dependent operation is cancelled or fails to complete its task successfully, it is up to you to decide whether the operation should continue. This may require you to incorporate some additional error-tracking capabilities into action objects.

KVO-Compliant Properties

Several properties of the NSOperation class conform to key-value encoding (KVC) and key-value observation (KVO). You can observe these properties to control other parts of the application as needed. To view properties, use the following key Paths:

  • isCancelled – read-only

  • isAsynchronous – read-only

  • isExecuting – read-only

  • isFinished – read-only

  • isReady – read-only

  • Dependencies – read-only // ⬆️⬆️ All the above are read-only

  • QueuePriority – readable and writable // Readable and writable

  • CompletionBlock – readable and writable // Readable and writable

While it is possible to attach observers to these attributes, they should not be bound to application user interface elements using Cocoa bindings. The code associated with the user interface can usually only be executed in the main thread of the application. Because an operation can be performed in any thread, the KVO notification associated with that operation can also occur in any thread.

If you provide a custom implementation for any of the above attributes, the implementation must remain KVC and KVO compatible. If you define other properties for NSOperation objects, it is recommended that you also make these properties compatible with KVC and KVO. For information on how to support key-value Coding, see key-value Coding Guide. For information on how to support key-value Observing, see the key-value Observing Programming Guide.

Multicore Considerations

The NSOperation class itself is multi-core aware. As a result, it is safe to call methods on an NSOperation object from multiple threads without creating additional locks to synchronize access to the object. This behavior is necessary because the operation usually runs in a different thread than the one that created and monitored it.

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

Asynchronous Versus Synchronous Operations

If you plan to execute the action object manually rather than adding it to the queue, you can design the operation to be performed synchronously or asynchronously. By default, action objects are synchronized. In a synchronous operation, the operation object does not create a separate thread to run its task. When you call the start method of a synchronous operation directly from your code, the operation will be executed immediately in the current thread. When the start method of such an object returns control to the caller, the task itself is complete.

When the start method of an asynchronous operation is called, it may return before the corresponding task has completed. An asynchronous operation object is responsible for scheduling its tasks on a separate thread. This operation can be performed by directly starting a new thread, calling an asynchronous method, or committing a block to a scheduling queue. In fact, it doesn’t matter if the operation is in progress when control is returned to the caller, just that it might be.

If you always plan to use queues to perform operations, it is easier to define them as synchronous. However, if you perform the operation manually, you may need to define the operation object as asynchronous. Defining asynchronous operations takes more work because you have to monitor the current state of the task and use KVO notifications to report changes in that state. However, defining asynchronous operations can be useful in situations where you need to ensure that manually performed operations do not block the calling thread. Manual operations default to synchronous or can be defined as asynchronous. When you use queues to perform operations, either synchronous or asynchronous execution depends on the queue itself.

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

For information on how to define synchronous and asynchronous operations, see subclass Description (NSInvocationOperation or NSBlockOperation).

Subclassing Notes (Some Notes about Subclassing NSOperation)

The NSOperation class provides the basic logic to track the execution state of an operation, but otherwise must be subclassed to do any real work. How you create subclasses depends on whether the operation is designed to be executed concurrently or not.

Methods to Override

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

  • main

In this method, you can place the code needed to perform a given task. Of course, you should also define a custom initialization method to make it easier to create instances of your custom classes. You may also need to define getter and setter methods to access data in an operation. However, if you do define custom getter and setter methods, you must ensure that they can be safely called from multiple threads.

If you want to create concurrent operations, 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. Either generating a thread or calling an asynchronous function can be done with this method. When starting an operation, the start method should also update the execution status of the operation reported by the Executing property. You can do this by sending a KVO notification to the executing key path to let interested clients know that the operation is running. The executing property must also provide state in a thread-safe manner.

After completing or canceling its task, the concurrent operation object must generate KVO notifications for isExecuting and isFinished Key Path to mark the final state change 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 report their completion before they can be removed from the queue. In addition to generating KVO notifications, the rewrite of the executing and FINISHED properties should continue to report the exact value based on the status of the operation.

For additional information and guidance on how to define concurrent operations, see Concurrency Programming Guide.

Important: In the start method, super should never be called. When you define a concurrent operation, you need to provide the same behavior as the default start method, including starting tasks and generating appropriate KVO notifications. Your start method should also check to see if the action itself is canceled before actually starting the task. For more information on cancellation semantics, refer to the Responding to the Cancel Command section below.

Even for concurrent operations, there is no need to override methods other than those described above. However, if you customize the dependent nature of the operation, you may have to override other methods and provide additional KVO notifications. For dependencies, this might only require notification for isReady Key Path. Because the Dependencies property contains a list of dependent operations, changes to it are already handled by the default NSOperation class.

Maintaining Operation Object States

Action objects maintain status information internally to determine when it is safe to perform and notify the external clients process during the life of the operation. Custom subclasses maintain this state information to ensure that operations in your code are performed correctly. Key Paths associated with operation state include:

  • IsReady isReady Key Path lets clients know when an operation can be performed. The Ready attribute contains the value true (when the operation is ready to be executed immediately), or false (if there are still unfinished operations that depend on it).

    In most cases, you don’t have to manage the state of this key path yourself. If the readiness of your operation is determined by factors that do not depend on the operation, then you can provide your own implementation of the ready property and track the readiness of the operation yourself. It is usually easier to create action objects only when the external state allows it.

    In macOS 10.6 and later, if an action is canceled while waiting for one or more related actions to complete, those related actions are ignored, and the value of this property is updated to reflect that it is now ready to run. This behavior gives the action queue an opportunity to clear canceled operations from its queue more quickly.

  • IsExecuting isExecuting Key Path lets clients know if an operation is processing its assigned task. The executing property must report true if the operation is working on its task, otherwise it must report false.

    If you replace the start method of an action object, you must also replace the executing property and generate a KVO notification when the execution status of the operation changes.

  • IsFinished isFinished Key Path lets clients know that the operation has successfully completed its task or that it has been canceled and is exiting. The dependency is not cleared until the value of isFinished Key Path is changed to true. Similarly, the operation queue does not unlist operations until the FINISHED property contains the value true. Therefore, marking operations as completed is critical to prevent queue backups from ongoing or cancelled operations.

    If you replace the start method or action object, you must also replace the FINISHED property and generate a KVO notification when the operation completes execution or cancellation.

  • IsCancelled isCancelled Key Path lets clients know that an operation has been requested to cancel. Support for cancellation is voluntary, but encouraged, and your own code does not have to send KVO notifications for this key path. The handling of cancellation notification in an operation is described in more detail in the Responding to the Cancel Command below.

Responding to the Cancel Command

Once an operation is added to the queue, it is out of your control. The queue takes over and schedules the task. However, if you later decide that you don’t want to perform the action because the user pressed the cancel button in the progress panel or quit the application, for example, you can cancel the action to prevent unnecessary CPU time. You can do this by calling the Cancel method on the operation object itself or by calling the cancelAllOperations method on the NSOperationQueue class.

Canceling an operation does not immediately force it to stop what it is doing. Although all operations need to consider the value in the Cancelled property, the code must explicitly check the value in this property and abort as needed. The default implementation of NSOperation (start) includes unchecking. For example, if the start method of an operation is canceled before it is called, the start method exits without starting the task.

Note: In macOS 10.6 and later, if you call the cancel method on an operation that is in an operation queue and has an unfinished dependency operation, those dependency operations are subsequently ignored. Because the operation has been canceled, this behavior allows the queue to call the start method of the operation to remove the operation from the queue without calling its main method. If the cancel method is called for an operation that is not in the queue, the operation is immediately marked as canceled. In each case, marking the action as ready or completed generates the corresponding KVO notification.

Cancellation semantics should always be supported in any custom code you write. In particular, the main task code should periodically check the value of the Cancelled property. If the property reports a value of YES, your action object should be cleaned up and exited as soon as possible. If you implement a custom start method, it should include early checks for cancellation and behave appropriately. Your custom start method must be prepared to handle such premature cancellations.

In addition to simply exiting when an action is cancelled, it is also important to move the cancelled action to the appropriate final state. Specifically, if you manage the values of the FINISHED and Executing properties yourself (probably because you are implementing a concurrent operation), you must update those properties accordingly. Specifically, you must change the value returned from FINISHED to YES and the value returned from execution to NO. These changes must be made even if the operation is cancelled before execution begins.

init

Returns an initialized NSOperation object.

- (id)init
Copy the code

Your custom subclass must call this method. The default implementation initializes the instance variable of the object and prepares it for use. This method runs on the current thread, which is used to allocate the action object.

start

The operation starts.

- (void)start;
Copy the code

The default implementation of this method updates the execution status of the operation and calls the main method of the Receiver. This method also performs several checks to ensure that the operation can actually run. For example, if receiver has been cancelled or completed, this method simply returns without calling main. (In OS X V10.5, this method will throw an exception if the operation has completed.) If the operation currently being performed or is not yet ready to execute, this method will cause NSInvalidArgumentException anomalies. In OS X V10.5, this method automatically catches and ignores any exceptions thrown by the main method. In macOS 10.6 and later, exceptions are allowed to propagate outside of this method. You should not allow exceptions to propagate from the main method.

Note: An operation is not considered ready if it still depends on other operations that have not yet completed.

If you want to implement concurrent operations, you must override this method and use it to start the operation. Your custom implementation cannot call super at any time. In addition to configuring the execution environment for the task, the implementation of this method must also track the state of the operation and provide appropriate state transitions. When the operation executes and then completes its work, it should generate KVO notifications for isExecuting and isFinished Key Path, respectively. For more information about manually generating KVO notifications, see the Key-value Observing Programming Guide.

If you want to perform the operation manually, you can call this method explicitly. However, it is the programmer’s fault to call this method on an action object already in the action queue or to queue the operation after calling this method. Once an action object is added to the queue, the queue assumes all responsibility for that action object.

main

Performs non-parallel tasks of the receiver.

- (void)main;
Copy the code

The default implementation of this method does nothing. You should override this method to perform the required tasks. Do not call super in your implementation. This method will be executed automatically in the auto-release pool provided by NSOperation, so there is no need to create your own auto-release pool block in the implementation.

You don’t need to override this method if you want to implement concurrent operations, but you can if you want to call it from the custom start method.

completionBlock

The block to be executed after the main task of the operation is complete.

@property (nullable, copy) void (^completionBlock)(void) API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
Copy the code

The completionBlock takes no arguments and returns no value.

The exact execution context of the completion block is not guaranteed, but it is usually a child thread. Therefore, you should not use this block to do anything that requires a very specific execution context. Instead, you should offload the work to the main thread of your application or a specific thread that can perform the work. For example, if you have a custom thread that is used to coordinate the completion of an operation, you can ping that thread using the completion block.

When the value in the FINISHED property changes to YES, the completionBlock you provide is executed. Because the completionBlock executes after the action indicates that it has completed its task, you cannot use other work queues that the completionBlock will be considered part of that task. The FINISHED property of an action object contains the value YES, which by definition must do all the work related to the task. You should use the completionBlock to notify the object of interest that the work is complete, or to perform other tasks that may be related to, but not part of, the actual task of the operation.

A completed operation may be completed by being canceled or by successfully completing its task. You should keep this in mind when writing block code. Likewise, you should make no assumptions about the successful completion of the operations, which themselves may have been cancelled.

This property is set to nil after completionBlock starts executing on iOS 8 and later and macOS 10.10 and later.

cancel

Advises the operating object to stop executing its tasks.

- (void)cancel;
Copy the code

This method does not force you to stop operating code. Instead, it updates the object’s internal flag to reflect the change in state. This method is invalid if the operation has completed execution. Cancelling an action that is currently in the action queue but has not yet been executed allows the action to be removed from the queue earlier than usual.

In macOS 10.6 and later, if an operation is in a queue, but waiting for an unfinished dependency operation, those operations are subsequently ignored. Because it has been canceled, this behavior allows the action queue to call the start method of the operation more quickly and clear the object from the queue. If you cancel an operation that is not in the queue, this method immediately marks the object as completed. In each case, marking the object as ready or complete generates the corresponding KVO notification.

In versions of macOS prior to 10.6, the action object remained in the queue until all its dependencies were removed through the normal process. Therefore, operations must wait until all of their dependent operations have finished executing, or they themselves have been cancelled, and their start methods are called.

cancelled

A Boolean value indicating whether an operation has been canceled.

@property (readonly, getter=isCancelled) BOOL cancelled;
Copy the code

The default value for this property is NO. Calling the cancel method on this object sets the value of this property to YES. After cancellation, the action must move to the finished state.

Cancelling does not actively prevent the execution of receiver’s code. The action object is responsible for periodically calling this method and stopping itself when the method returns YES.

You should always check the value of this property before completing the action task, which usually means checking it at the beginning of the custom main method. An operation can be cancelled before execution begins or at any time during execution. Therefore, checking the value at the beginning of the main method (and checking periodically in that method) can exit as soon as the operation is canceled.

executing

A Boolean value indicating whether the operation is currently being performed.

@property (readonly, getter=isExecuting) BOOL executing;
Copy the code

If the operation is currently performing its primary task, the value of this property is YES; Otherwise, the value is NO.

When implementing concurrent operation objects, the implementation of this property must be overridden to return the execution status of the operation. In a custom implementation, a KVO notification must be generated for isExecuting Key PATH whenever the execution state of an action object changes. For more information about manually generating KVO notifications, see the Key-value Observing Programming Guide.

There is no need to re-implement this property for non-concurrent operations.

finished

A Boolean value indicating whether an operation has completed performing its task.

@property (readonly, getter=isFinished) BOOL finished;
Copy the code

If the operation has completed its primary task, the value of this property is YES; If the operation is performing the task or has not started the task, the value of this property is NO.

When implementing concurrent operation objects, the implementation of this property must be overridden to return the completion status of the operation. In a custom implementation, KVO notifications must be generated for isFinished Key Path whenever the completion state of the operation object changes. For more information about manually generating KVO notifications, see the Key-value Observing Programming Guide.

There is no need to re-implement this property for non-concurrent operations.

concurrent

A Boolean value indicating whether an operation performs its task asynchronously.

@property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below
Copy the code

Use the asynchronous property instead.

This property has the value YES for operations that run asynchronously relative to the current thread; This property has the value NO for operations that run synchronously on the current thread. The default value for this property is NO.

In macOS 10.6 and later, the action queue ignores the value in this property and always starts the action on a separate thread.

asynchronous

A Boolean value indicating whether an operation performs its task asynchronously.

@property (readonly, getter=isAsynchronous) BOOL asynchronous API_AVAILABLE(macos(10.8), ios(7.0), watchos(2.0), tvos(9.0));
Copy the code

This property has the value YES for operations that run asynchronously relative to the current thread; This property has the value NO for operations that run synchronously on the current thread. The default value for this property is NO.

When implementing asynchronous manipulation objects, you must implement this property and return YES.

ready

A Boolean value indicating whether the operation can be performed immediately.

@property (readonly, getter=isReady) BOOL ready;
Copy the code

The ready states of operations depend on their dependencies on other operations, and may depend on custom conditions you define. The NSOperation class manages dependencies on other operations and reports on the readiness of receivers based on these dependencies.

If you want to use a custom condition to define the ready state of an action object, reimplement this property and return a value that accurately reflects the ready state of the recipient. To do so, your custom implementation must take the default property value from super and merge the ready value into the new value of the property. In a custom implementation, KVO notifications must be generated for isReady Key Path whenever the ready state of the operation object changes.

name

Name of the operation.

@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
Copy the code

Assign a name to the action object to help identify it during debugging.

addDependency:

Makes the receiver dependent on the completion of the specified operation.

- (void)addDependency:(NSOperation *)op;
Copy the code

Operation: Operation that the receiver depends on. The same dependency should not be added to the receiver more than once, and the result of doing so is uncertain.

The Receiver is not considered ready until all related operations of the receiver have been executed. Adding a dependency has no practical effect if the receiver is already performing its task. This method may change the isReady and Dependencies properties of the receiver.

It is a programmer’s fault to create any circular dependencies between a set of operations. Doing so can result in deadlocks between operations and possibly freeze programs.

removeDependency:

Removes the receiver’s dependency on the specified operation.

- (void)removeDependency:(NSOperation *)op;
Copy the code

Operation: Removes related operations from the receiver.

This method may change the isReady and Dependencies properties of the receiver.

dependencies

Array of action objects that must be executed before the current object can begin execution.

@property (readonly, copy) NSArray<NSOperation *> *dependencies;
Copy the code

This property contains an array of NSOperation objects. To add objects to this array, use the addDependency: method.

An action object must execute after all its dependent operations have completed execution. The operation is not removed from the dependency list when it completes execution. You can use this list to keep track of all related actions, including those that have been completed. The only way to remove an action from the list is to use the removeDependency: method.

qualityOfService

The relative importance of allocating system resources to this operation. (Priority of the thread when the operation is performed)

typedef NS_ENUM(NSInteger, NSQualityOfService) {
    NSQualityOfServiceUserInteractive = 0x21,
    NSQualityOfServiceUserInitiated = 0x19,
    NSQualityOfServiceUtility = 0x11,
    NSQualityOfServiceBackground = 0x09,
    NSQualityOfServiceDefault = - 1
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
Copy the code

Service levels Indicates the priority of the operation object to access system resources, such as CPU time, network resources, and disk resources. Operations with higher quality of service levels have a higher priority than system resources, so they can perform tasks faster. You use Service levels to ensure that actions responding to explicit user requests take precedence over less critical work.

This attribute reflects the minimum service levels required to perform the operation effectively. The default value of this attribute is NSQualityOfServiceBackground, you should keep the value as much as possible. When changing service levels, use the lowest level appropriate for performing the task. For example, if the user start the task and wait for the task to complete, please send values NSQualityOfServiceUserInteractive assigned to this attribute. If resources are available, the system can provide higher service levels for operations. see Prioritize Work with Quality of Service Classes in Energy Efficiency Guide for iOS Apps and Prioritize Work at the Task Level in Energy Efficiency Guide for Mac Apps.

queuePriority

The execution priority of the operation in the operation queue.

// These constants allow you to distinguish the order in which operations are performed.
You can use these constants to specify the relative order of operations waiting to start in the action queue. You should always use these constants (not defined values) for prioritization.
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

@property NSOperationQueuePriority queuePriority;
Copy the code

This property contains the relative priority of the operation. This value is used to influence the order in which operations are listed and executed. The return value always corresponds to one of the predefined constants. (For a list of valid values, see NSOperationQueuePriority.) If not explicitly set priority, then this method returns NSOperationQueuePriorityNormal.

You should only classify the relative priority of non-dependent operations using priority values as needed. Priority values should not be used to manage dependencies between different operation objects. If you need to establish dependencies between operations, use the addDependency: method instead.

If you try to specify one of the priority values and define constants don’t match (8 l, 4 l, 0, 4, 8), the operation object will automatically adjust your specified value, make it towards NSOperationQueuePriorityNormal priority, and effective constant values in the first place to stop. For example, if the specified value – 10, the operation will adjust the value to match the NSOperationQueuePriorityVeryLow constants. Similarly, if you specify a + 10, this will adjust the value to match the NSOperationQueuePriorityVeryHigh constants.

waitUntilFinished

Prevents the current thread from executing until the action object completes its task.

- (void)waitUntilFinished API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
Copy the code

An action object must never call this method on itself, and should avoid calling this method on any operation submitted to the same action queue as itself. Doing so will result in an operation deadlock. Instead, other parts of the application may call this method as needed to prevent other tasks from completing before the target action object completes. It is usually safe to call this method on operations in different operation queues, although deadlocks can still be created if each operation is waiting on another operation.

A typical use of this method is to call it from the code that created the operation in the first place. After submitting an operation to the queue, you call this method to wait for the operation to complete execution.

So much for NSOperation, let’s look at its two subclasses: NSBlockOperation and NSInvocationOperation.

NSBlockOperation

An operation that manages the concurrent execution of one or more blocks.

API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0))
@interface NSBlockOperation : NSOperation
Copy the code

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 without having to create a separate action object for each block. When multiple blocks are executed, the operation itself is considered completed only if all blocks have completed.

Blocks added to an operation are scheduled to the appropriate work queue with default priority. The block itself should not make any assumptions about the configuration of its execution environment.

blockOperationWithBlock:

Creates and returns an NSBlockOperation object and adds the specified block to the object.

+ (instancetype)blockOperationWithBlock:(void(^) (void))block;
Copy the code

Block: Block to be added to the list of new block action objects. The block should have no arguments and no return value.

addExecutionBlock:

Adds the specified block to the list of blocks to be executed by the receiver.

- (void)addExecutionBlock:(void(^) (void))block;
Copy the code

Block: Block to be added to the receiver list. The block should have no arguments and no return value.

A specified block should make no assumptions about its execution environment. In the receiver is performing or has completed this method is called when the trigger NSInvalidArgumentException anomalies.

executionBlocks

The block associated with the receiver.

@property (readonly, copy) NSArray<void(^) (void)> *executionBlocks;
Copy the code

The blocks in this array are copies of the blocks that were originally added using the addExecutionBlock: method.

NSInvocationOperation

An operation that manages the execution of a single encapsulation task designated as Invocation.

API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0))
NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available")
@interface NSInvocationOperation : NSOperation
Copy the code

The NSInvocationOperation class is a concrete subclass of NSOperation, and you can use it to initiate an operation that involves calling a selector on a specified object. This class implements non-concurrent operations.

initWithTarget:selector:object:

Returns an NSInvocationOperation object initialized with the specified target and Selector.

- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
Copy the code

Target: Defines the object that specifies the selector. Sel: selector to call when an operation is run. A selector can take either 0 or 1 arguments. If it accepts a parameter, the parameter must be of type ID. The return type of this method can be void, a scalar value, or an object that can be returned as an ID type. Arg: Parameter object to be passed to SEL. If sel takes no arguments, specify nil.

Return Value: the initialized NSInvocationOperation object; Nil if the target object does not implement the specified selector (sel).

If a non-void return type is used to specify selector, the return value can be obtained by calling the result method after the operation has completed execution. The Receiver tells the Invocation object to reserve its parameters.

initWithInvocation:

Returns the NSInvocationOperation object initialized with the specified Invocation object.

- (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;
Copy the code

Inv: Call object, used to identify target object, selector, and parameter object.

Return Value: the initialized NSInvocationOperation object; If it cannot be initialized, nil is returned.

This method is the specified initializer. The Receiver tells the NSInvocation object to keep its arguments (the retainArguments function of the NSInvocation class).

invocation

NSInvocation of receiver.

@property (readonly, retain) NSInvocation *invocation;
Copy the code

NSInvocation object, which identifies the target object, selector, and the parameters used to perform the operation task.

result

The result of the Invocation or method.

@property (nullable, readonly, retain) id result;
Copy the code

Method or an NSValue object containing a non-returned value, if not an object. Null if the method or call has not completed execution.

If an exception is thrown during the execution of a method or call, accessing this property will raise it again. Accessing this property throws an exception if the operation is canceled, or if the return type of the call or method is void; Otherwise, return false. See Result Exceptions.

Result Exceptions

The name of the exception thrown by NSInvocationOperation if an error occurs when calling the Result method.

FOUNDATION_EXPORT NSExceptionName const NSInvocationOperationVoidResultException API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
FOUNDATION_EXPORT NSExceptionName const NSInvocationOperationCancelledException API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

NSInvocationOperationVoidResultException if calls for a void return type method invocation result method, is the name of an exception is thrown.

NSInvocationOperationCancelledException if cancel the call result method after operation, will throw an exception names.

So that’s NSBlockOperation and NSInvocationOperation, but let’s take a look at the more important operation queue, the NSOperationQueue class.

NSOperationQueue

The queue in which canonical operations (NSOperations) are performed.

API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0))
@interface NSOperationQueue : NSObject <NSProgressReporting>
Copy the code

Action queues execute their queued NSOperation objects based on their priority and readiness. After an action is added to an action queue, it remains in its queue until it reports completion of the task. After adding an operation, you cannot remove it directly from the queue.

Note: The action queue will hold operations until the operation is complete, and the queue itself will hold operations until all operations are complete. Pausing the operation queue with an unfinished operation can cause a memory leak.

Determining the Execution Order

The operations in the queue are organized according to their readiness, priority, and interoperable dependencies, and perform accordingly. If all queued operations have the same queuePriority and are ready to execute when they are queued (that is, their ready attribute returns YES), they will be executed in the order they were submitted to the queue. Otherwise, the action queue always performs the operation that has the highest priority relative to other ready operations.

However, you should never rely on queue semantics to ensure that operations are executed in a particular order, because changes in the state of operation readiness can alter the order of execution generated. Interop dependencies provide an absolute order of execution for operations, even if they are in different operation queues. An action object is not considered ready to execute until all its dependent operations have been executed.

Canceling the operation.

Completing its task does not necessarily mean that the operation completes the task; Operations can also be cancelled. Canceling the action object leaves the object in the queue, but notifies the object that it should stop its task as soon as possible. For the currently executing operation, this means that the working code of the operation object must check the cancel status, stop the ongoing operation, and mark itself as completed. For operations that have been queued but not yet performed, the queue must still call the start method of the operation object so that it can handle the cancellation event and mark itself as completed.

Note: Canceling an operation causes the operation to ignore any dependencies it may have. This behavior enables the queue to execute the start method of the operation as soon as possible. Use the start method in turn to move the operations to the finished state so that they can be removed from the queue.

Kvo-compliant Properties (Compliant to KVO)

The NSOperationQueue class conforms to key-value encoding (KVC) and key-value observation (KVO). You can observe these properties as needed to control other parts of the application. To observe properties, use the following critical path:

  • operations – read-only

  • OperationCount – read-only // ⬆️⬆️ read only

  • MaxConcurrentOperationCount – readable and writable / / ⬇ ️ ⬇ ️ can read can write

  • suspended – readable and writable

  • name – readable and writable

Although you can attach observers to these properties, you should not use Cocoa Binding to bind them to elements of the application user interface. The code associated with the user interface can usually only be executed in the main thread of the application. However, KVO notifications associated with an action queue can occur in any thread.

Thread Safety

It is safe to use a single NSOperationQueue object in multiple threads without creating additional locks to synchronize access to the object.

Action queues use the Dispatch framework to initiate the execution of their operations. As a result, the operation is always executed on a separate thread regardless of whether the operation is specified as synchronous or asynchronous.

mainQueue

Returns the operation queue associated with the main thread.

@property (class, readonly, strong) NSOperationQueue *mainQueue API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
Copy the code

Return Value: The default operation queue bound to the main thread.

The returned queue performs one operation at a time on the main thread of the application. Actions performed on the main thread are interwoven with other tasks that must be performed on the main thread, such as event services and updates to the application user interface. The queue performs these operations in RunLoop common modes, as shown in the NSRunLoopCommonModes constant. The value of the underlyingQueue attribute of the queue is the scheduling queue of the main thread; You cannot set this property to any other value.

currentQueue

Returns the action queue that started the current operation.

@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
Copy the code

Return Value: Operation queue to start an operation, or nil if the queue cannot be determined.

You can use this method from a running action object to get a reference to the action queue that started it. Calling this method from outside the context of the running operation usually results in returning nil.

addOperation:

Adds the specified operation (NSOperation) to the receiver.

- (void)addOperation:(NSOperation *)op;
Copy the code

Op: Operation to add to the queue.

Once added, the specified operation remains in the queue until execution is complete.

Most Important: an action object only operate in a queue, if the operation is already in another queue, then this method will trigger a NSInvalidArgumentException anomalies. Similarly, if the current operation is executed or has been completed, this method will cause NSInvalidArgumentException anomalies.

addOperations:waitUntilFinished:

Adds the specified operation to the queue.

- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
Copy the code

Ops: Operations to be added to the queue. Wait: If YES, blocks the current thread until all specified operations have completed. If it is NO, the operation is added to the queue, and control is immediately returned to the caller.

An operation object can be in a maximum of one operation queue at a time. If the operation object is currently being executed or completed, it cannot be added. If for any operation of ops parameters, any one of these error conditions is true, then this method will trigger a NSInvalidArgumentException anomalies.

Once added, the specified operation remains in the queue until its FINISHED method returns YES.

addOperationWithBlock:

Wraps the specified block in the operation and adds it to the receiver.

- (void)addOperationWithBlock:(void(^) (void))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
Copy the code

Block: Block executed from an operation. The block takes no arguments and returns no value.

This method first adds a single block to the receiver by wrapping a single block into the action object. You should not attempt to get a reference to a newly created action object or determine its type information.

cancelAllOperations

Cancel all queued and ongoing operations.

- (void)cancelAllOperations;
Copy the code

This method calls the cancel method for all current operations in the queue.

A cancel operation does not automatically remove it from the queue, nor does it stop the operation currently in progress. For an action queued for execution, the queue must still attempt to execute the action before it recognizes that it has been canceled and moves it to the completed state. For actions that have been performed, the action object itself must check to cancel and stop the action in progress so that it can move to the finished state. In both cases, the completed (or cancelled) operation still has a chance to execute its completion block before it is removed from the queue.

waitUntilAllOperationsAreFinished

Blocks the current thread until all receivers have been queued and the ongoing operation is complete.

- (void)waitUntilAllOperationsAreFinished;
Copy the code

When called, the method blocks the current thread and waits for the current and queued operations of the receiver to complete execution. When the current thread is blocked, the receiver continues to start the queued operation and monitors the ongoing operation. During this time, the current thread cannot add operations to the queue, but other threads can. This method returns once all pending operations have completed.

If there are no operations in the queue, this method returns immediately.

qualityOfService

Default Service level applied to operations performed using queues.

@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
Copy the code

This property specifies the service level to be applied to the action object added to the queue. This value is used if the action object has an explicit service level set. The default value for this property depends on how the queue is created. For you create a queue, the default value is NSOperationQualityOfServiceBackground. For mainQueue method returns the queue, the default value is NSOperationQualityOfServiceUserInteractive, cannot be changed.

Service levels Indicates the priority of the operation object to access system resources, such as CPU time, network resources, and disk resources. Operations with higher quality of service levels have higher priority than system resources, so they can perform tasks faster. You use Service levels to ensure that actions responding to explicit user requests take precedence over less critical work.

maxConcurrentOperationCount

The maximum number of queued operands that can be executed simultaneously.

@property NSInteger maxConcurrentOperationCount;
Copy the code

The values in this property affect only the simultaneous operations of the current queue. Other operation queues can also perform their maximum number of operations in parallel.

Reducing the number of concurrent operations does not affect any operations currently being performed. Specified values NSOperationQueueDefaultMaxConcurrentOperationCount (it is recommended to use this value) will make the system based on the system conditions set the maximum operand.

The default value of this property for NSOperationQueueDefaultMaxConcurrentOperationCount. You can use key-value observation to monitor changes to this property value. Configuration observers to monitor the operation of the queue maxConcurrentOperationCount key path.

NSOperationQueueDefaultMaxConcurrentOperationCount

The default maximum operand to execute simultaneously in a queue.

static const NSInteger NSOperationQueueDefaultMaxConcurrentOperationCount = - 1;
Copy the code

This number is dynamically determined based on current system conditions.

suspended

A Boolean value indicating whether the queue is actively scheduling an operation to be performed.

@property (getter=isSuspended) BOOL suspended;
Copy the code

When this property has a value of NO, the queue actively initiates the operation that is to be performed in the queue. Setting this property to YES prevents the queue from starting any queuing operations, but the operations that have been performed continue to be executed. You can continue to add operations to the pending queue, but they are not scheduled until you change this property to NO.

The operation is removed from the queue only when it has finished executing. However, in order to complete the execution, you must first start the operation. Because a suspended queue does not start any new operations, it does not delete any operations (including canceled operations) that are currently queued and not performed.

You can use key-value observation to monitor changes to this property value. Configure an observer to monitor the SUSPENDED key path of the action queue.

The default value for this property is NO.

name

The name of the operation queue.

@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
Copy the code

Name gives you a way to identify the operation queue at run time. The tool can also use the name to provide additional context during debugging or analyzing code.

The default value for this property is a string containing the memory address of the operation queue. You can use key-value observation to monitor changes to this property value. Configure the observer to monitor the name key path of the action queue.

underlyingQueue

A scheduling queue used to perform operations.

@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
Copy the code

The default value for this property is nil. You can set the value of this property to an existing scheduling queue so that queued operations are scattered among blocks submitted to that scheduling queue.

The value of this property should be set only if there are no operations in the queue; When operationCount is not equal to zero setting the value of this attribute will trigger a NSInvalidArgumentException. The value of this property cannot be the value returned by dispatch_get_main_queue. The quality-of-service level set for the underlying scheduling queue overrides any values set for the qualityOfService property of the operation queue.

Note: If OS_OBJECT_IS_OBJC is YES, this attribute automatically reserves its allocated queue.

progress

Represents the total progress of the operations performed in the queue.

@property (readonly, strong) NSProgress *progress API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0), watchos(6.0));
Copy the code

The progress property represents the total progress of the operations performed in the queue. By default, NSOperationQueue does not report progress until the totalUnitCount of progress is set. After setting the totalUnitCount property for the progress, the queue will choose to participate in the progress report. When enabled, each operation that completes before main ends contributes 1 unit of completion to the overall progress of the queue (overriding start without calling super does not contribute to progress). When updating the progress totalUnitCount, special attention should be paid to match conditions and care should be taken to avoid “falling behind”. For example, when the NSOperationQueue progress is 5/10, indicating 50% complete, and there are more than 90 operations to add, totalUnitCount will make the progress report 5/100, representing 5%. In this example, this means that any progress bar will jump from showing 50% to 5%, which is probably undesirable. In cases where totalUnitCount needs to be adjusted, it is recommended to do so by using the addBarrierBlock: API to ensure thread-safety. This ensures that no unexpected execution state will occur, allowing for potential backward-moving schedule scenarios.

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.progress.totalUnitCount = 10;
Copy the code

addBarrierBlock:

When NSOperationQueue has completed all enqueueing operations, the addBarrierBlock: method executes the block and prevents any subsequent operations from being executed until the Barrier block is complete. This behavior is similar to the dispatch_barrier_async function.

- (void)addBarrierBlock:(void(^) (void))barrier API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0), watchos(6.0));
Copy the code

Refer to the link

Reference link :🔗

  • NSOperation
  • NSBlockOperation
  • NSInvocationOperation
  • NSOperationQueue
  • Operation Queues
  • IOS multithreading: summary of NSOperation, NSOperationQueue
  • Concurrent programming: apis and challenges