iOS interview questions (6)

DispatchGroup and OperationQueue, Concurrency, multithreading
Recently i had to do a bunch of asynchronous tasks and i just wanted to get notified when all of them finish. This blog is going to cover two easy ways of doing this: DispatchGroup and…

iOS 2018 Series: Cracking iOS interview or Become iOS expert (6)

Chapter 6: DispatchGroup and OperationQueue

Recently i had to do a bunch of asynchronous tasks and i just wanted to get notified when all of them finish. This blog is going to cover two easy ways of doing this: DispatchGroup and OperationQueue. We’ll cover them both in this post.

DispatchGroup

If you’re working on a project that’s not using Operations and you don’t want to refactor any of the existing code,DispatchGroup is a powerful API that lets you group together these tasks into one task.

Situation 1: Calculate sum of three arrays on three different threads and once all thread completes its execution, i want to use that result.

Before considering the solution for it, lets learn some of the API’s related to dispatch group.

enter() — Explicitly indicates that a block has entered the group.

leave() - Explicitly indicates that a block in the group has completed.

func notify(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, queue: DispatchQueue, execute work: @escaping @convention(block) () -> Swift.Void) -
Schedules a block to be submitted to a queue with a specified quality of service class and configuration when a group of previously submitted block objects have completed.

Solution: These three api will help me to handle above situation, lets learn how to use them.

I have written below function for adding elements of array and return

This function create thread using DispatchQueue.global().async and create loop for adding all elements of infoArr using DispatchQueue.concurrentPerform (similar to for loop) then returning the completion handler named block().

Below function will create three threads in parallel which calculate sum and once thread finished, it will call notify function of dispatchgroup.

Once the notify function called, we will have all three array sum and which we can pass in completionBlock().

Block calling order — Console log
Output logs

Operation & Operation Queue

Its an abstract class, you need to subclass it to use. If you just want to execute a small piece of code or call a method you can use BlockOperation and NSInvocationOperation instead of subclassing Operation.

Situation 2: Calculate sum of three arrays on three different threads using operation and once all thread completes its execution, i want to use that result.

Solution: Create OperationQueue instance to addOperations, create three BlockOperation for adding sum of array, then add dependency between operation (as we need to get final sum actually they are not dependent), Once we add these three block operations in any order to operation queue, it doesnot matter and register the completion block on Block three to get final sum.

Output: Below images indicate — Sequence of calling blocks and final sum result

Concurrent Queue with Barrier

Tasks in concurrent queues execute in any order and can start simultaneously. This is very fast compared to the above serial queue. However, because we may be reading while writing at the same time, we will run in to the readers-writers problem.

What if there was a way that you can ensure no writing occurs while reading and no reading occurs while writing using concurrency? Well, there is a way to this using Concurrent Queue with Barrier. If we take the concurrent code above, and insert a barrier to the write operation, we ensure that the writing will occur after all the reading in the queue is performed and that no reading occurs while writing.

Using concurrent queues with barriers helps us improve and speed up our code while eliminating the readers-writers problem, which is also important for singletons.

Bonus time: Difference between #import and @class

“#import” brings the entire header file in question into the current file; any files that THAT file #imports are also included.

@class, on the other hand (when used on a line by itself with some class names), just tells the compiler “Hey, you’re going to see a new token soon; it’s a class, so treat it that way).

This is very useful when you’ve got the potential for ‘circular includes’; ie, Object1.h makes reference to Object2, and Object2.h makes reference to Object1. If you #import both files into the other, the compiler can get confused as it tries to #import Object1.h, looks in it and sees Object2.h; it tries to #import Object2.h, and sees Object1.h, etc.

If, on the other hand, each of those files has @class Object1; or @class Object2;, then there's no circular reference. Just be sure to actually #import the required headers into your implementation (.m) files.

mutating keyword

Modifying Value Types from Within Instance Methods

Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.

However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicit self property, and this new instance will replace the existing one when the method ends.

You can opt in to this behavior by placing the mutating keyword before the func keyword for that method