Concurrency in Swift (Custom Operations Part 4)
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.
When you add an operation to an operation queue, the queue ignores the value of the asynchronous property and always calls the start method from a separate thread. Therefore, if you always run operations by adding them to 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
Operation State
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 →The 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 →The 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
Summary
When operation starts its state progresses from isReady to 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
领英推荐
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
Thread Safety
As shown in the previous example one bug can be thatstate 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.
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 0 raises an 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 invalidArgumentException