NSOperation Class Reference

Inherits from
Conforms to
Framework
/System/Library/Frameworks/Foundation.framework
Availability
Available in OS X v10.5 and later.
Companion guide
Declared in
NSOperation.h
Related sample code

Overview

The NSOperation class is an abstract class you use to encapsulate the code and data associated with a single task. Because it is abstract, you do not use this class directly but instead subclass or use one of the system-defined subclasses (NSInvocationOperation or NSBlockOperation) to perform the actual task. Despite being abstract, the base implementation of NSOperation does include significant logic to coordinate the safe execution of your task. The presence of this built-in logic allows you to focus on the actual implementation of your task, rather than on the glue code needed to ensure it works correctly with other system objects.

An operation object is a single-shot object—that is, it executes its task once and cannot be used to execute it again. You typically execute operations by adding them to an operation queue (an instance of the NSOperationQueue class). An operation queue executes its operations either directly, by running them on secondary threads, or indirectly using the libdispatch library (also known as Grand Central Dispatch). For more information about how queues execute operations, see NSOperationQueue Class Reference.

If you do not want to use an operation queue, you can execute an operation yourself by calling its start method directly from your code. Executing operations manually does put more of a burden on your code, because starting an operation that is not in the ready state triggers an exception. The isReady method reports on the operation’s readiness.

Operation Dependencies

Dependencies are a convenient way to execute operations in a specific order. You can add and remove dependencies for an operation using the addDependency: and removeDependency: methods. By default, an operation object that has dependencies is not considered ready until all of its dependent operation objects have finished executing. Once the last dependent operation finishes, however, the operation object becomes ready and able to execute.

The dependencies supported by NSOperation make no distinction about whether a dependent operation finished successfully or unsuccessfully. (In other words, canceling an operation similarly marks it as finished.) It is up to you to determine whether an operation with dependencies should proceed in cases where its dependent operations were cancelled or did not complete their task successfully. This may require you to incorporate some additional error tracking capabilities into your operation objects.

KVO-Compliant Properties

The NSOperation class is key-value coding (KVC) and key-value observing (KVO) compliant for several of its properties. As needed, you can observe these properties to control other parts of your application. The properties you can observe include the following:

  • isCancelled - read-only property

  • isConcurrent - read-only property

  • isExecuting - read-only property

  • isFinished - read-only property

  • isReady - read-only property

  • dependencies - read-only property

  • queuePriority - readable and writable property

  • completionBlock - readable and writable property

Although you can attach observers to these properties, you should not use Cocoa bindings to bind them to elements of your application’s user interface. Code associated with your user interface typically must execute only in your application’s main thread. Because an operation may execute in any thread, KVO notifications associated with that operation may similarly occur in any thread.

If you provide custom implementations for any of the preceding properties, your implementations must maintain KVC and KVO compliance. If you define additional properties for your NSOperation objects, it is recommended that you make those properties KVC and KVO compliant as well. For information on how to support key-value coding, see Key-Value Coding Programming Guide. For information on how to support key-value observing, see Key-Value Observing Programming Guide.

Multicore Considerations

The NSOperation class is itself multicore aware. It is therefore safe to call the methods of an NSOperation object from multiple threads without creating additional locks to synchronize access to the object. This behavior is necessary because an operation typically runs in a separate thread from the one that created and is monitoring it.

When you subclass NSOperation, you must make sure that any overridden methods remain safe to call from multiple threads. If you implement custom methods in your subclass, such as custom data accessors, you must also make sure those methods are thread-safe. Thus, access to any data variables in the operation must be synchronized to prevent potential data corruption. For more information about synchronization, see Threading Programming Guide.

Concurrent Versus Non-Concurrent Operations

If you plan on executing an operation object manually, instead of adding it to a queue, you can design your operation to execute in a concurrent or non-concurrent manner. Operation objects are non-concurrent by default. In a non-concurrent operation, the operation’s task is performed synchronously—that is, the operation object does not create a separate thread on which to run the task. Thus, when you call the start method of a non-concurrent operation directly from your code, the operation executes immediately in the current thread. By the time the start method of such an object returns control to the caller, the task itself is complete.

In contrast to a non-concurrent operation, which runs synchronously, a concurrent operation runs asynchronously. In other words, when you call the start method of a concurrent operation, that method could return before the corresponding task is completed. This might happen because the operation object created a new thread to execute the task or because the operation called an asynchronous function. It does not actually matter if the operation is ongoing when control returns to the caller, only that it could be ongoing.

If you always plan to use queues to execute your operations, it is simpler to define them as non-concurrent. If you execute operations manually, though, you might want to define your operation objects as concurrent to ensure that they always execute asynchronously. Defining a concurrent operation requires more work, because you have to monitor the ongoing state of your task and report changes in that state using KVO notifications. But defining concurrent operations can be useful in cases where you want to ensure that a manually executed operation does not block the calling thread.

For information on how to define both concurrent and non-concurrent operations, see the subclassing notes.

Subclassing Notes

The NSOperation class provides the basic logic to track the execution state of your operation but otherwise must be subclassed to do any real work. How you create your subclass depends on whether your operation is designed to execute concurrently or non-concurrently.

Methods to Override

For non-concurrent operations, you typically override only one method:

Into this method, you place the code needed to perform the given task. Of course, you should also define a custom initialization method to make it easier to create instances of your custom class. You might also want to define getter and setter methods to access the data from the operation. However, if you do define custom getter and setter methods, you must make sure those methods can be called safely from multiple threads.

If you are creating a concurrent operation, you need to override the following methods at a minimum:

In a concurrent operation, your start method is responsible for starting the operation in an asynchronous manner. Whether you spawn a thread or call an asynchronous function, you do it from this method. Upon starting the operation, your start method should also update the execution state of the operation as reported by the isExecuting method. You do this by sending out KVO notifications for the isExecuting key path, which lets interested clients know that the operation is now running. Your isExecuting method must also return the status in a thread-safe manner.

Upon completion or cancellation of its task, your concurrent operation object must generate KVO notifications for both the isExecuting and isFinished key paths to mark the final change of state for your operation. (In the case of cancellation, it is still important to update the isFinished key path, even if the operation did not completely finish its task. Queued operations must report that they are finished before they can be removed from a queue.) In addition to generating KVO notifications, your overrides of the isExecuting and isFinished methods should also continue to return accurate values based on the state of your operation.

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

Even for concurrent operations, there should be little need to override methods other than those described above. However, if you customize the dependency features of operations, you might have to override additional methods and provide additional KVO notifications. In the case of dependencies, this would likely only require providing notifications for the isReady key path. Because the dependencies property is used to manage the list of dependent operations, changes to it are already handled by the default NSOperation class.

Maintaining Operation Object States

Operation objects maintain state information internally to determine when it is safe to execute and also to notify external clients of the progression through the operation’s life cycle. Your custom subclasses must maintain this state information to ensure the correct execution of operations in your code. Table 1 lists the key paths associated with an operation’s states and how you should manage that key path in any custom subclasses.

Table 1  Key paths for operation object states

Key Path

Description

isReady

The isReady key path lets clients know when an operation is ready to execute. The isReady method returns YES to indicate that the operation is ready to execute now or NO if there are still unfinished operations on which it is dependent.

In most cases, you do not have to manage the state of this key path yourself. If the readiness of your operations is determined by factors other than dependent operations, however—such as by some external condition in your program—you can provide your own implementation of the isReady method and track your operation’s readiness yourself. It is often simpler though just to create operation objects only when your external state allows it.

In OS X v10.6 and later, if you cancel an operation while it is waiting on the completion of one or more dependent operations, those dependencies are thereafter ignored and the value of this property is updated to reflect that it is now ready to run. This behavior gives an operation queue the chance to flush cancelled operations out of its queue more quickly.

isExecuting

The isExecuting key path lets clients know whether the operation is actively working on its assigned task. The isExecuting method must return YES if it is working on its task or NO if it is not.

If you replace the start method of your operation object, you must also replace the isExecuting method and generate KVO notifications when the execution state of your operation changes.

isFinished

The isFinished key path lets clients know that an operation finished its task successfully or was cancelled and is exiting. An operation object does not clear a dependency until the value at the isFinished key path changes to YES. Similarly, an operation queue does not dequeue an operation until the isFinished method returns YES. Thus, marking operations as finished is critical to keeping queues from backing up with in-progress or cancelled operations.

If you replace the start method or your operation object, you must also replace the isFinished method and generate KVO notifications when the operation finishes executing or is cancelled.

isCancelled

The isCancelled key path lets clients know that the cancellation of an operation was requested. Support for cancellation is voluntary but encouraged and your own code should not have to send KVO notifications for this key path. The handling of cancellation notices in an operation is described in more detail in “Responding to the Cancel Command.”

Responding to the Cancel Command

Once you add an operation to a queue, the operation is out of your hands. The queue takes over and handles the scheduling of that task. However, if you decide later that you do not want to execute the operation after all—because the user pressed a cancel button in a progress panel or quit the application, for example—you can cancel the operation to prevent it from consuming CPU time needlessly. You 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 what it is doing. Although respecting the value returned by the isCancelled is expected of all operations, your code must explicitly check the value returned by this method and abort as needed. The default implementation of NSOperation does include checks for cancellation. For example, if you cancel an operation before its start method is called, the start method exits without starting the task.

You should always support cancellation semantics in any custom code you write. In particular, your main task code should periodically check the value of the isCancelled method. If the method ever returns YES, your operation object should clean up and exit as quickly as possible. If you implement a custom start method, that method should include early checks for cancellation and behave appropriately. Your custom start method must be prepared to handle this type of early cancellation.

In addition to simply exiting when an operation is cancelled, it is also important that you move a cancelled operation to the appropriate final state. Specifically, if you manage the values for the isFinished and isExecuting properties yourself (perhaps because you are implementing a concurrent operation), you must update those variables accordingly. Specifically, you must change the value returned by isFinished to YES and the value returned by isExecuting to NO. You must make these changes even if the operation was cancelled before it started executing.

Tasks

Initialization

Executing the Operation

Canceling Operations

Getting the Operation Status

Managing Dependencies

Prioritizing Operations in an Operation Queue

Managing the Execution Priority

Waiting for Completion

Instance Methods

addDependency:

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

- (void)addDependency:(NSOperation *)operation
Parameters
operation

The operation on which the receiver should depend. The same dependency should not be added more than once to the receiver, and the results of doing so are undefined.

Discussion

The receiver is not considered ready to execute until all of its dependent operations have finished executing. If the receiver is already executing its task, adding dependencies has no practical effect. This method may change the isReady and dependencies properties of the receiver.

It is a programmer error to create any circular dependencies among a set of operations. Doing so can cause a deadlock among the operations and may freeze your program.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

cancel

Advises the operation object that it should stop executing its task.

- (void)cancel
Discussion

This method does not force your operation code to stop. Instead, it updates the object’s internal flags to reflect the change in state. If the operation has already finished executing, this method has no effect. Canceling an operation that is currently in an operation queue, but not yet executing, makes it possible to remove the operation from the queue sooner than usual.

In OS X v10.6 and later, if an operation is in a queue but waiting on unfinished dependent operations, those operations are subsequently ignored. Because it is already cancelled, this behavior allows the operation queue to call the operation’s start method sooner and clear the object out of the queue. If you cancel an operation that is not in a queue, this method immediately marks the object as finished. In each case, marking the object as ready or finished results in the generation of the appropriate KVO notifications.

In versions of OS X prior to 10.6, an operation object remains in the queue until all of its dependencies are removed through the normal processes. Thus, the operation must wait until all of its dependent operations finish executing or are themselves cancelled and have their start method called.

For more information on what you must do in your operation objects to support cancellation, see “Responding to the Cancel Command.”

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

completionBlock

Returns the block to execute when the operation’s main task is complete.

- (void (^)(void))completionBlock
Return Value

The block to execute after the operation’s main task is completed. This block takes no parameters and has no return value.

Discussion

The completion block you provide is executed when the value returned by the isFinished method changes to YES. Thus, this block is executed by the operation object after the operation’s primary task is finished or cancelled.

Availability
  • Available in OS X v10.6 and later.
Declared In
NSOperation.h

dependencies

Returns a new array object containing the operations on which the receiver is dependent.

- (NSArray *)dependencies
Return Value

A new array object containing the NSOperation objects.

Discussion

The receiver is not considered ready to execute until all of its dependent operations finish executing.

Operations are not removed from this dependency list as they finish executing. You can therefore use this list to track all dependent operations, including those that have already finished executing. The only way to remove an operation from this list is to use the removeDependency: method.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

init

Returns an initialized NSOperation object.

- (id)init
Return Value

The initialized NSOperation object.

Discussion

Your custom subclasses must call this method. The default implementation initializes the object’s instance variables and prepares it for use.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

isCancelled

Returns a Boolean value indicating whether the operation has been cancelled.

- (BOOL)isCancelled
Return Value

YES if the operation was explicitly cancelled by an invocation of the receiver’s cancel method; otherwise, NO. This method may return YES even if the operation is currently executing.

Discussion

Canceling an operation does not actively stop the receiver’s code from executing. An operation object is responsible for calling this method periodically and stopping itself if the method returns YES.

You should always call this method before doing any work towards accomplishing the operation’s task, which typically means calling it at the beginning of your custom main method. It is possible for an operation to be cancelled before it begins executing or at any time while it is executing. Therefore, calling this method at the beginning of your main method (and periodically throughout that method) lets you exit as quickly as possible when an operation is cancelled.

Availability
  • Available in OS X v10.5 and later.
See Also
Declared In
NSOperation.h

isConcurrent

Returns a Boolean value indicating whether the operation runs asynchronously.

- (BOOL)isConcurrent
Return Value

YES if the operation runs asynchronously with respect to the current thread or NO if the operation runs synchronously on whatever thread started it. This method returns NO by default.

Discussion

If you are implementing a concurrent operation, you must override this method and return YES from your implementation. For more information about the differences between concurrent and non-concurrent operations, see “Concurrent Versus Non-Concurrent Operations.”

In OS X v10.6 and later, operation queues ignore the value returned by this method and always start operations on a separate thread.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

isExecuting

Returns a Boolean value indicating whether the operation is currently executing.

- (BOOL)isExecuting
Return Value

YES if the operation is executing; otherwise, NO if the operation has not been started or is already finished.

Discussion

If you are implementing a concurrent operation, you should override this method to return the execution state of your operation. If you do override it, be sure to generate KVO notifications for the isExecuting key path whenever the execution state of your operation object changes. For more information about manually generating KVO notifications, see Key-Value Observing Programming Guide.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

isFinished

Returns a Boolean value indicating whether the operation is done executing.

- (BOOL)isFinished
Return Value

YES if the operation is no longer executing; otherwise, NO.

Discussion

If you are implementing a concurrent operation, you should override this method and return a Boolean to indicate whether your operation is currently finished. If you do override it, be sure to generate appropriate KVO notifications for the isFinished key path when the completion state of your operation object changes. For more information about manually generating KVO notifications, see Key-Value Observing Programming Guide.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

isReady

Returns a Boolean value indicating whether the receiver’s operation can be performed now.

- (BOOL)isReady
Return Value

YES if the operation can be performed now; otherwise, NO.

Discussion

Operations may not be ready due to dependencies on other operations or because of external conditions that might prevent needed data from being ready. The NSOperation class manages dependencies on other operations and reports the readiness of the receiver based on those dependencies.

If you want to use custom conditions to determine the readiness of your operation object, you can override this method and return a value that accurately reflects the readiness of the receiver. If you do so, your custom implementation should invoke super and incorporate its return value into the readiness state of the object. Your custom implementation must also generate appropriate KVO notifications for the isReady key path.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

main

Performs the receiver’s non-concurrent task.

- (void)main
Discussion

The default implementation of this method does nothing. You should override this method to perform the desired task. In your implementation, do not invoke super.

If you are implementing a concurrent operation, you are not required to override this method but may do so if you plan to call it from your custom start method.

Availability
  • Available in OS X v10.5 and later.
See Also
Related Sample Code
Declared In
NSOperation.h

queuePriority

Returns the priority of the operation in an operation queue.

- (NSOperationQueuePriority)queuePriority
Return Value

The relative priority of the operation. The returned value always corresponds to one of the predefined constants. (For a list of valid values, see “Operation Priorities.”) If no priority is explicitly set, this method returns NSOperationQueuePriorityNormal.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

removeDependency:

Removes the receiver’s dependence on the specified operation.

- (void)removeDependency:(NSOperation *)operation
Parameters
operation

The dependent operation to be removed from the receiver.

Discussion

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

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

setCompletionBlock:

Sets the block to execute when the operation has finished executing.

- (void)setCompletionBlock:(void (^)(void))block
Parameters
block

The block to be executed when the operation finishes. This method creates a copy of the specified block. The block itself should take no parameters and have no return value.

Discussion

The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context. Instead, you should shunt that work to your application’s main thread or to the specific thread that is capable of doing it. For example, if you have a custom thread for coordinating the completion of the operation, you could use the completion block to ping that thread.

Because the completion block executes after the operation indicates it has finished its task, you must not use a completion block to queue additional work considered to be part of that task. An operation object whose isFinished method returns YES must be done with all of its task-related work by definition. The completion block should be used to notify interested objects that the work is complete or perform other tasks that might be related to, but not part of, the operation’s actual task.

A finished operation may finish either because it was cancelled or because it successfully completed its task. You should take that fact into account when writing your block code. Similarly, you should not make any assumptions about the successful completion of dependent operations, which may themselves have been cancelled.

Availability
  • Available in OS X v10.6 and later.
Declared In
NSOperation.h

setQueuePriority:

Sets the priority of the operation when used in an operation queue.

- (void)setQueuePriority:(NSOperationQueuePriority)priority
Parameters
priority

The relative priority of the operation. For a list of valid values, see “Operation Priorities.”

Discussion

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

If you attempt to specify a priority value that does not match one of the defined constants, this method automatically adjusts the value you specify towards the NSOperationQueuePriorityNormal priority, stopping at the first valid constant value. For example, if you specified the value -10, this method would adjust that value to match the NSOperationQueuePriorityVeryLow constant. Similarly, if you specified +10, this method would adjust the value to match the NSOperationQueuePriorityVeryHigh constant.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

setThreadPriority:

Sets the thread priority to use when executing the operation.

- (void)setThreadPriority:(double)priority
Parameters
priority

The new thread priority, specified as a floating-point number in the range 0.0 to 1.0, where 1.0 is the highest priority.

Discussion

The value you specify is mapped to the operating system’s priority values. The specified thread priority is applied to the thread only while the operation’s main method is executing. It is not applied while the operation’s completion block is executing. For a concurrent operation in which you create your own thread, you must set the thread priority yourself in your custom start method and reset the original priority when the operation is finished.

Availability
  • Available in OS X v10.6 and later.
See Also
Declared In
NSOperation.h

start

Begins the execution of the operation.

- (void)start
Discussion

The default implementation of this method updates the execution state of the operation and calls the receiver’s main method. This method also performs several checks to ensure that the operation can actually run. For example, if the receiver was cancelled or is already finished, this method simply returns without calling main. (In OS X v10.5, this method throws an exception if the operation is already finished.) If the operation is currently executing or is not ready to execute, this method throws an NSInvalidArgumentException exception. In OS X v10.5, this method catches and ignores any exceptions thrown by your main method automatically. In OS X v10.6 and later, exceptions are allowed to propagate beyond this method. You should never allow exceptions to propagate out of your main method.

If you are implementing a concurrent operation, you must override this method and use it to initiate your operation. Your custom implementation must not call super at any time. In addition to configuring the execution environment for your task, your implementation of this method must also track the state of the operation and provide appropriate state transitions. When the operation executes and subsequently finishes its work, it should generate KVO notifications for the isExecuting and isFinished key paths respectively. For more information about manually generating KVO notifications, see Key-Value Observing Programming Guide.

You can call this method explicitly if you want to execute your operations manually. However, it is a programmer error to call this method on an operation object that is already in an operation queue or to queue the operation after calling this method. Once you add an operation object to a queue, the queue assumes all responsibility for it.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

threadPriority

Returns the thread priority to use when executing the operation.

- (double)threadPriority
Return Value

A floating-point number in the range 0.0 to 1.0, where 1.0 is the highest priority. The default thread priority is 0.5.

Availability
  • Available in OS X v10.6 and later.
Declared In
NSOperation.h

waitUntilFinished

Blocks execution of the current thread until the receiver finishes.

- (void)waitUntilFinished
Discussion

The receiver should never call this method on itself and should avoid calling it on any operations submitted to the same operation queue as itself. Doing so can cause the operation to deadlock. It is generally safe to call this method on an operation that is in a different operation queue, although it is still possible to create deadlocks if each operation waits on the other.

A typical use for this method would be to call it from the code that created the operation in the first place. After submitting the operation to a queue, you would call this method to wait until that operation finished executing.

Availability
  • Available in OS X v10.6 and later.
Declared In
NSOperation.h

Constants

NSOperationQueuePriority

Describes the priority of an operation relative to other operations in an operation queue.

typedef NSInteger NSOperationQueuePriority;
Discussion

For a list of related constants, see “Operation Priorities”.

Availability
  • Available in OS X v10.5 and later.
Declared In
NSOperation.h

Operation Priorities

These constants let you prioritize the order in which operations execute.

enum {
   NSOperationQueuePriorityVeryLow = -8,
   NSOperationQueuePriorityLow = -4,
   NSOperationQueuePriorityNormal = 0,
   NSOperationQueuePriorityHigh = 4,
   NSOperationQueuePriorityVeryHigh = 8
};
Constants
NSOperationQueuePriorityVeryLow

Operations receive very low priority for execution.

Available in OS X v10.5 and later.

Declared in NSOperation.h.

NSOperationQueuePriorityLow

Operations receive low priority for execution.

Available in OS X v10.5 and later.

Declared in NSOperation.h.

NSOperationQueuePriorityNormal

Operations receive the normal priority for execution.

Available in OS X v10.5 and later.

Declared in NSOperation.h.

NSOperationQueuePriorityHigh

Operations receive high priority for execution.

Available in OS X v10.5 and later.

Declared in NSOperation.h.

NSOperationQueuePriorityVeryHigh

Operations receive very high priority for execution.

Available in OS X v10.5 and later.

Declared in NSOperation.h.

Discussion

You can use these constants to specify the relative ordering of operations that are waiting to be started in an operation queue. You should always use these constants (and not the defined value) for determining priority.

Declared In
NSOperation.h