Concurrency in Swift (Operations and Operation Queue Part 3)
In this part we will cover following topics
Operations
Operations are an object-oriented way to encapsulate work that you want to perform asynchronously. Operations are designed to be used either in conjunction with an operation queue or by themselves
An operation object is an instance of the Operation or NSOperation class (in the Foundation framework) that you use to encapsulate work you want your application to perform.
The Operation class itself is an abstract base class that must be subclassed in order to do any useful work. Despite being abstract, this class does provide a significant amount of infrastructure to minimize the amount of work you have to do in your own subclasses. In addition, the Foundation framework provides two concrete subclasses that you can use as-is with your existing code.
Operations State
An operation has a state machine that represents its lifecycle. There are several possible states that occur at various parts of this lifecycle:
There are mainly three ways to create operations
1. BlockOperation (Concrete Class)
A class you use as-is to execute one or more block objects concurrently. Because it can execute more than one block, a block operation object operates using a group semantic; only when all of the associated blocks have finished executing is the operation itself considered finished.. In block operation you can take advantage of operation dependencies, KVO, notifications and cancelling .
As shown in Figure 1 we executed this code async which means it will return immediately but the bad news is it will block the main thread since operation.start() was called on main thread
Operation objects execute in a synchronous manner by default — that is, they perform their task in the thread that calls their start method.
What the heck is synchronous manner and execute one or more block objects concurrently.
As shown in Figure 1.0.1 as you can see the tasks/blocks added to the Block operation itself executed concurrently but the block run synchronous manner means it blocked the thread at which start is called in our case it is main thread
As shown in Figure Figure 1.0.2 , since we call start method on other thread , it will block that thread
As shown in Figure Figure 1.0.3, we can add completion block as well which will call when all concurrent blocks will executed
Run Block Operation Concurrently
As shown in Figure 1.1 since we call start() method on a background thread it will perform their task in the thread. There is a cool way to do this using operation queue and we will see this later.
2. NSInvocationOperation (Concrete Class)
A class you use as-is to create an operation object based on an object and selectorfrom your application.
In objective C we can create NSInvocationOperation while it’s not available in Swift.
3. Custom Operations
领英推荐
Subclassing Operation gives you complete control over the implementation of your own operations, including the ability to alter the default way in which your operation executes and reports its status.
As shown in Figure 2 we created custom operations by subclass it from Operation base class and override its main method. When you subclass you put your task on main method. We implemented non concurrent custom operation and in this case we blocked the main thread
If you plan to execute operations manually and still want them to run asynchronously, you must take the appropriate actions to ensure that they do. You do this by defining your operation object as a concurrent operation.
As shown in Figure 3 we performed the following steps to perform task concurrently
Operation Queues
Add Operations
As shown in Figure 4 we created two operations (using Block) and added them into operation queue. Operation queue started both operation on some background thread and executed them. No need to call start() method on custom thread ??. When we add operation to the operation queue it run as soon as it’s ready
As shown in Figure 5 we just executed task serially or you can say we implemented serial queue using Operation Queues please refer to my part 1if you don’t know what is serial queue by setting maxConcurrentOperationCount= 1
maxConcurrentOperationCount →The maximum number of queued operations that can execute at the same time. The default value is -1 which means let the system decide
By setting maxConcurrentOperationCount = 2 we made a concurrent queue and now tasks are executing concurrently as shown in Figure 6
Operations Dependencies
As shown in Figure 7 we again created a serial queue by adding dependencies between two tasks. We created two block operations and we are saying that don’t start task 1 until task 2 is finished by calling blockOperations1.addDependency(blockOperations2)
Dispatch Group Implementation Using Operations Queue
we used GCD dispatch group feature to block a thread until one or more tasks finished executing. As shown in Figure 8 we implemented the same behaviour using Operation Queues by using dependencies. This is very helpful if you cannot make progress until all of the specified tasks are completed.
As shown in Figure 8 we have three tasks and we wanted to run concurrently and when all the tasks finished we need to call some method to indicate that all tasks has finished and what we did
As shown in Figure 9 we just block the main thread by setting waitUntilFinished = true. So the question is when it is helpful and you will get the answer in the next section
As shown in Figure 10 we implemented a dispatch group behavior using operation queue without using any dependencies what we did we used waitUntilFinished feature appropriately . If you are on background thread you can block this thread to achieve this behaviour. I intentionally switched to background thread using DispatchQueue.global().
We told operation queue run task 1, task 2 and task 3 on operation queue and block the current thread until these concurrent tasks will finish their execution
Benefits of Operation Queues Over GCD