As shown in Figure 1 we created two operations and we added both in operation queue, In addition to this, we added a dependency rule that say starts operation 2 after finishes operation 1. As we know the previous blog, the operation queue automatically runs the operation on another thread
When you add an operation to an operation queue, the queue ignores the value of the
asynchronousproperty and always calls the
startmethod from a separate thread. Therefore, if you always run operations by adding them to an operation queue, there is no reason to make them asynchronous.
As shown in Figure 2 even though we added the dependency rule still, task 2 started and the reason is that task 1 dispatch its task on another queue and return immediately and the operation queue assume that it finishes that’s why it started task2 , and in a real application, we faced this issue a lot since we used NSUrlSession that executes its task on another queue so te question is how can we solve this problem
We can solve this problem by changing the operation state manually. The
NSOperation class provides the basic logic to track the execution state of your operation automatically but since our task is dispatching on another queue, the operation queue needs to know when the task actually finished
The operation has a more complex lifecycle then Dispatch groups. This gives you greater control. 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 maintains this state information to ensure the correct execution of operations in your code. The key paths associated with an operation’s states are:
isReady key path lets clients know when an operation is ready to execute. The
ready property contains the value
true when the operation is ready to execute now or
false 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
ready property and track your operation’s readiness yourself. It is often simpler though just to create operation objects only when your external state allows it.
isExecuting →T he
isExecuting key path lets clients know whether the operation is actively working on its assigned task. The
executing the property must report the value
true if the operation is working on its task or
false if it is not.
isFinished → The
isFinished key path lets clients know that the operation finished its task successfully or was canceled and is exiting. An operation object does not clear a dependency until the value at the
isFinished key path changes to
true. Similarly, an operation queue does not dequeue an operation until the
finished property contains the value
true. Thus, marking operations as finished is critical to keeping queues from backing up with in-progress or canceled operations.
isCancelled key path lets clients know that the cancellation of 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
When operation starts its state progresses from
isExecuting. If the task is asynchronous like download an image, it sends the call to the network and returns immediately. It looks like it finished. It’s no longer doing any work on the current thread but the async task is running on the background thread. You need a way to manually set the operation state to
isExecuting until it really finishes.
Operation State property are read only. You can’t set them directly , So hhow you manage their values for an async operation . You must do something to cause the operation state property to return the correct value. The operation class relies on , KVO (key value observation) to send the notification for state
Create an Asynchronous Operation
As shown in Figure 3 we created reusable async operation. Following thing to be consider
- First, we created a state enum that maintains the state of our async operation. The operation state property is read-only, you can’t set them directly Instead you’ll create a state property for your async operation, then use this property to manage the base class operation states properties. Your async operation needs a variable property to manage its state
- An asyn operation default initial state value is ready. Now we can override base class state properties to use new state property. Apple document says don’t override
isReady, the operation itself decides when the operation is Ready by examining its dependencies . In most cases, you do not have to manage the state of this
keypathyourself. 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 ready property and track your operation’s readiness yourself. It is often simpler though just to create operation objects only when your external state allows it.
- You just need to set the operation state properties
isFinishedto use your new state
- When implementing async operation object you must implment
isAsynchronousproperty and return true. This property is only used if you run the operation manually outside of an operation queue. It ensures the operation runs off the main thread.
- Operation uses
KVOto keep track of its state so your async operation must send KVO notifications whenever its state values change because it’s property is Read-only. We added property observers on state property and called
didChangeValueto its base class to update its state. In
willSet()we inform the operation queue that the property for the current state and the next state will change. In
didSetwe then inform the operation queue that the property for the previous state and the new state has changed.
- KVO mission accomplished.
- Since your async operation must trigger KVO notification for the base class properties which has is Prefix. so we created computed properties
- Next, we need to override the operation
startmethod checks if the operation has been
canceledand set our async operation state to
finished. If the operation is not canceled called the
mainfunction members this is an async operation that returns immediately, so you need to manually set its state to
- Do not call
super.start()from your override of
start. As the documentation for start says 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.
As shown in Figure 4 Now in specific async operation we just need to override
main and called finished. Now as you can see operation 2 starts after the operation 2 finishes irrespective of its task executed on another thread. This is one of the use cases of using custom/subclass operation. In addition to this since we add operation on operation queue, operation queue as usual use another thread to run the main method of operation
As shown in Figure 5 operation queue uses one thread since these operations are dependent it smartly can use one thread for these tasks since it is dependent. Note thread reference can be swap but it will ask only one thread from shared pools of thread
As shown in Figure 6, we removed dependency then you can see it asks for two threads. Now you can imagine how smart it is
As shown in the previous example one bug can be that
state is not thread-safe, setting
state might cause Thread Sanitizer issues. Also, Apply suggests that the overridden property must also provide the status in a thread-safe manner. As shown in Figure 7 we make state property atomic manner. If you don’t know what is a barrier please see previous part
As shown in Figure 8.1 and 8.2 we provided queue to operation queue and you can see
ourProvidedOperationQueueCustomQueue is used by operation queue for dispatching its task.
underlyingQueue →The dispatch queue used to execute operations.The default value of this property is
nil. You can set the value of this property to an existing dispatch queue to have enqueued operations interspersed with blocks submitted to that dispatch queue.The value of this property should only be set if there are no operations in the queue; setting the value of this property when operationCount is not equal to
invalidArgumentException. The value of this property must not be the value returned by dispatch_get_main_queue(). The quality-of-service level set for the underlying dispatch queue overrides any value set for the operation queue's qualityOfService property.
As shown in Figure 9 setting the value of
underlyingQueue property when operation count is not equal to
0 (means after we added operation to the operation queue) raises an
Asynchronous operations for writing concurrent solutions in Swift - SwiftLee
Asynchronous operations allow executing long-running tasks without having to block the calling thread until the…
Asynchronous Operations in Swift
This is the first part of two dealing with how to handle preloading of SpriteKit assets in Swift using an…
Key-Value Coding and Observing
Key-value coding and key-value observing are two formalized mechanisms that allow us to simplify our code by harnessing…
Thread example is taken from this link