Search Your Question

Difference between OperationQueue and DispatchGroup

Ans : 

You probably found yourself in a situation where you had to do a bunch of asynchronous tasks and you just wanted to get notified when all of them finish. There are two easy ways of doing this: DispatchGroup and OperationQueue. 

DispatchGroup :

DispatchGroups is probably the easiest way for you to know when a bunch of asynchronous calls is finished.  DispatchGroup is a part of GCD and in Swift 3 we got a nice swiftified GCD.

func getMovies(onCompleted: (([MovieItem]) -> ())?) {
    
    var result: [MovieItem] = []
    
    let dispatchGroup = DispatchGroup()
    
    for i in 1...5 {
        guard
            let urlString = DataSourceConstants.URLString(forPage: "\(i)")
            else {
                continue
        }
        
        dispatchGroup.enter()
        self.networkingProvider.restCall(urlString: urlString) {
            
            (responseObject) in
            
            guard
                let responseData = responseObject,
                let jsonObject = try? JSONSerialization.jsonObject(with: responseData, options: [JSONSerialization.ReadingOptions.allowFragments])
                else {
                    dispatchGroup.leave()
                    return
            }
            
            let movies = self.moviesFactory.movieItems(withJSON: jsonObject)
            result = result + movies
            
            dispatchGroup.leave()
        }
    }
    
    dispatchGroup.notify(queue: DispatchQueue.global()) {
        onCompleted?(result)
    }

}

Here we create your DispatchGroup at the top of the function and just before you execute an async piece of code, you enter the group. In your closure, you leave the same group just before the closure completes. When your DispatchGroup is empty, a closure will be called.

What’s really important here is the enter-leave pairs. You have to be very careful and make sure that you leave the group. It would be easy to introduce a bug in the code above. Let’s say that we didn’t leave the group in that guard statement above, just before the return. If the API called failed, or the JSON was malformed, the number of group entries would not match the number of leaves. So the group completion handler would never get called. If you’re calling this method from the UI and displaying an activity indicator while your networking requests are running, you would never get a callback from the method, and you would keep on spinning.

OperationQueue :

Operation queues are great and all, but if you just want to know when your queue is finished you won’t find a ready-made API waiting for you.

There is a simple trick you can use to get notified when your async tasks are finished. The trick is to use dependencies. You have two options here. If you need your operations to execute one after another, you can set the next operation to be dependent on the previous. So when your last operation is finished, your queue is finished as well. This is easy to do.

For concurrent operations(executing at same time),  Create another operation. Operations can have dependencies on multiple operations. So when you create your operations you add them as a dependency to that operation. When all dependent operation finish, your operation will get executed. And this way you can tell that your ‘queue’ is finished. If you think about this from a logical perspective, it makes perfect sense. Anyone can add a bunch of operations in the operation queue, and if you don’t own it completely, you don’t know who added what. So having a callback that will tell you that the queue is empty would not be very useful. Dependencies are baked into the Operations.

func getMovies(onCompleted: (([MovieItem]) -> ())?) {
    
    operationQueue.cancelAllOperations()
    
    var result: [MovieItem] = []
    
    let queueCompletionOperation = BlockOperation {
        onCompleted?(result)
    }
    
    var operations: [Operation] = []
    
    operationQueue.isSuspended = true
    
    for i in 1...5 {
        guard
            let urlString = DataSourceConstants.URLString(forPage: "\(i)")
            else {
                continue
        }
        
        let networkingOperation = GetDataOperation(withURLString: urlString, andNetworkingProvider: networkingProvider)
        let parsingOperation = ParseDataOperation(withFactory: moviesFactory)
        
        networkingOperation.completionBlock = {
            parsingOperation.moviesData = networkingOperation.responseData
        }
        
        parsingOperation.completionBlock = {
            if let moviesArray = parsingOperation.movies {
                DispatchQueue.global().sync(flags: .barrier) {
                    result = result + moviesArray
                }
            }
        }
        
        parsingOperation.addDependency(networkingOperation)
        
        queueCompletionOperation.addDependency(parsingOperation)
        
        operations.append(contentsOf: [parsingOperation, networkingOperation])
    }
    
    operations.append(queueCompletionOperation)
    
    operationQueue.addOperations(operations, waitUntilFinished: false)
    operationQueue.isSuspended = false
}

We have our ‘queueCompletionOperation’ at the top and in the block we’re calling our closure. We suspend the queue before adding new operations and we’re setting the dependency on our queueCompletionOperation to parsingOperation. After the loop is finished, we add all the operations in the queue and un-suspend the queue.


Dispatch queue :

DispatchQueue is an abstraction layer on top of the GCD queue that allows you to perform tasks asynchronously and concurrently in your application. Tasks are always executed in the order they’re added to the queue.
There are two types of dispatch queues :
1. Serial Dispatch Queue
2. Concurrent Dispatch Queue


What is output of following code?

Ans:

1.

var a = 6;
var b = 9;
let newClouser: ()->() = {[a,b] in
print(a+b)
}

a=3
b=3
newClouser()

Ans : 15
Explanation : We can't change value of a and b for closure. Reassignment will create new reference. We have captured value of a and b as we have used capture list ( [a,b] ). Capture list takes value of a and b as point of time.

If we have not used capture list ( [] ) then changing value for a and b affecting though value changing after closure declaration.

2.

print("1")
DispatchQueue.main.async {
    print("2")
    DispatchQueue.main.async {
        print(3)
    }
    print("4")
}
print("5")

Ans :
1
5
2
4
5

Explanation : 

You will always get 1, 2, 4, 3. The 5 will always be after the 1. But where it ends up in relation to the others depends on what queue the whole thing started on.

If this is started from the main queue then 5 will always be between 1 and 2.

Here's why:  This code starts on the main queue. 1 is printed. You then enqueue another block to run asynchronously on the main queue so that block will be run after the current block completes and the main queue gets to the end of the current run loop. The code continues to the next line which is to print 5. The current block ends and the next block on the main queue is run. This is the block of the first call to DispatchQueue.main.async. As this block runs it prints 2 (so now we have 1 5 2). Another block is enqueued to the main queue just like the last one. The current block continues and prints 4 (so now we have 1 5 2 4). The block ends and the next block on the main queue is run. This is the final block we added. That block runs and it prints 3 giving the final output of 1 5 2 4 3.

3.

for i in 0...10 {
    DispatchQueue.main.async {
        DispatchQueue.main.async {
            print("Print1",i)
        }
        print("print2",i)
    }
}

Ans : 

Print2 0
Print2 1
Print2 2
Print2 3
Print2 4
Print2 5
Print2 6
Print2 7
Print2 8
Print2 9
Print2 10
Print1 0
Print1 1
Print1 2
Print1 3
Print1 4
Print1 5
Print1 6
Print1 7
Print1 8
Print1 9
Print1 10

Explanation : Read explanation-2

4.

for i in 0...10 {
    DispatchQueue.main.async {
        DispatchQueue.main.sync {
            print("Print1",i)
        }
        print("Print2",i)
    }
}

Ans :

Deadlock occurs. May be crashed.

Explanation : Dispatch.main is a serial queue which has single thread to execute all the operations. If we call "sync" on this queue it will block all other operations currently running on the thread and try to execute the code block inside sync whatever you have written. This results in "deadlock".

This is a deadlock. It crashes because GCD detects this particular form of deadlock and aborts the program.

5.

for i in 0...10 {
    DispatchQueue.main.async {
        DispatchQueue.global().sync {
            print("Print1",i)
        }
        print("Print2",i)
    }
}

Ans : 

Print1 0
Print2 0
Print1 1
Print2 1
Print1 2
Print2 2
Print1 3
Print2 3
Print1 4
Print2 4
Print1 5
Print2 5
Print1 6
Print2 6
Print1 7
Print2 7
Print1 8
Print2 8
Print1 9
Print2 9
Print1 10
Print2 10

Explanation : 

6. 

for i in 0...10 {
    DispatchQueue.main.async {
        DispatchQueue.global().async {
            print("Print1",i)
        }
        print("Print2",i)
    }
}

Ans : 

Print2 0
Print1 0
Print2 1
Print1 1
Print2 2
Print1 2
Print2 3
Print1 3
Print2 4
Print1 4
Print2 5
Print2 6
Print1 5
Print1 6
Print2 7
Print1 7
Print2 8
Print1 8
Print2 9
Print1 9
Print2 10
Print1 10

Explanation : 

7. 

for i in 0...10 {
    DispatchQueue.global().async {
        DispatchQueue.global().async {
            print("Print1",i)
        }
        print("Print2",i)
    }
}

Ans :

Order is not fixed. Print in any order.

Explanation : 

8. 

for i in 0...10 {
    DispatchQueue.global().sync {
        DispatchQueue.global().sync {
            print("Print1",i)
        }
        print("Print2",i)
    }
}

Ans :

Print1 0
Print2 0
Print1 1
Print2 1
Print1 2
Print2 2
Print1 3
Print2 3
Print1 4
Print2 4
Print1 5
Print2 5
Print1 6
Print2 6
Print1 7
Print2 7
Print1 8
Print2 8
Print1 9
Print2 9
Print1 10
Print2 10

Explanation : 

9. 

for i in 0...10 {
    DispatchQueue.global().async {
        DispatchQueue.main.async {
            print("Print1",i)
        }
        print("Print2",i)
    }
}

Ans :
Print2 5
Print2 4
Print2 8
Print2 7
Print2 10
Print2 3
Print2 6
Print2 2
Print2 1
Print2 0
Print2 9
Print1 3
Print1 4
Print1 0
Print1 5
Print1 6
Print1 2
Print1 1
Print1 7
Print1 8
Print1 9
Print1 10

First all print from Print2 in random order, after that, Print1 printed in random order.

10. 

for i in 0...10 {
    DispatchQueue.global().sync {
        DispatchQueue.main.async {
            print("Print1",i)
        }
        print("Print2",i)
    }
}

Ans :

Print2 0
Print2 1
Print2 2
Print2 3
Print2 4
Print2 5
Print2 6
Print2 7
Print2 8
Print2 9
Print2 10
Print1 0
Print1 1
Print1 2
Print1 3
Print1 4
Print1 5
Print1 6
Print1 7
Print1 8
Print1 9
Print1 10

Explanation :

11.

for i in 0...10 {
    print("Manan1")
    DispatchQueue.main.async {
        print("Manan2")
        DispatchQueue.main.async {
            print("Print1",i)
        }
        print("print2",i)
    }
}

Ans : 

Manan1
Manan1
Manan1
Manan1
Manan1
Manan1
Manan1
Manan1
Manan1
Manan1
Manan1
Manan2
print2 0
Manan2
print2 1
Manan2
print2 2
Manan2
print2 3
Manan2
print2 4
Manan2
print2 5
Manan2
print2 6
Manan2
print2 7
Manan2
print2 8
Manan2
print2 9
Manan2
print2 10
Print1 0
Print1 1
Print1 2
Print1 3
Print1 4
Print1 5
Print1 6
Print1 7
Print1 8
Print1 9
Print1 10

Explanation :

What are different type of Queues in GCD?

Ans :

Queue : 

A Queue is a linear structure that follows the First In First Out (FIFO) order. Here we are going to use two types of queue Serial queue and Concurrent queue.

Serial Queue : 


Serial Queue

In the serial queue, only one task runs at a time. Once the first task ends then only the second task will begin. All the task follow the same sequence they have to wait until running task finish.

Create our own serial queue using GCD:


let serialQueue = DispatchQueue(label: "mySerialQueue")
 serialQueue.async {
  // Add your serial task

 }

Download images using serial queue : 


let myArray = [img1, img2, img3, img4, img5, img6]
   
   for i in 0 ..< myArray.count {
     serialQueue.async {
       do {
         let data = try Data(contentsOf: URL(string: myArray[i])!)
         if let image = UIImage(data: data) {
           DispatchQueue.main.async {
             self.imageSerial[i].image = image
           }
         }
       } catch {
         print("error is \(error.localizedDescription)")
       }
     }

   }


Concurrent Queue : 


Concurrent Queue

In the Concurrent queue, multiple tasks can run at the same time. The start time of the task will be the order they are added, means Task 0 start first then Task 1 will start after that and so on. Tasks can finish in any order.

Global queue is example of Concurrent queue.


// Synchronous
DispatchQueue.global().sync {
 // write your code here
}

// Asynchronous
DispatchQueue.global().async {
 // write your code here
}

Let's create own concurrent queue using GCD: 


let concurrentQueue = DispatchQueue(label: "myConcurrentQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)

 concurrentQueue.async {
  // Add your concurrent task
 }

Download images using concurrent queue:

let myArray = [img1, img2, img3, img4, img5, img6]

for i in 0 ..< myArray.count {
  concurrentQueue.async {
    do {
      let data = try Data(contentsOf: URL(string: myArray[i])!)
      if let image = UIImage(data: data) {
        DispatchQueue.main.async {
          self.imageConcurrent[i].image = image
        }
      }
    } catch {
      print("error is \(error.localizedDescription)")
    }
  }
}


What is QOS?

Ans : 

QOS - Quality of Services

A quality-of-service (QoS) class categorizes work to be performed on a DispatchQueue. By specifying the quality of a task, you indicate its importance to your app. When scheduling tasks, the system prioritizes those that have higher service classes.

Because higher priority work is performed more quickly and with more resources than lower priority work, it typically requires more energy than lower priority work. Accurately specifying appropriate QoS classes for the work your app performs ensures that your app is responsive and energy efficient.

Levels of Priority
After an app kicks off and starts a runloop on the main thread, one can begin taking advantage of QoS. QoS breaks out priorities into four different groups. Each one corresponds to common tasks one might find themselves coding in their iOS endeavors.
  • User Interactive: Work that happens on the main thread, such as animations or drawing operations.
  • User Initiated: Work that the user kicks off and should yield immediate results. This work must be completed for the user to continue.
  • Utility: Work that may take a bit and doesn’t need to finish right away. Analogous to progress bars and importing data.
  • Background: This work isn’t visible to the user. Backups, syncs, indexing, etc.


There are five global queues having quality of service level ranging  from high performance to high energy efficiency. 
  1. DispatchQueue.global(qos: .userInteractive) - highest priority
  2. DispatchQueue.global(qos: .userInitiated)
  3. DispatchQueue.global() // default
  4. DispatchQueue.global(qos: .utility)
  5. DispatchQueue.global(qos: .background) - lowest priority 
DispatchQueue.global(qos: .userInteractive).async {
 // Event handling task
}


What is mutating in swift?

Ans :

If you have ever tried using the mutating keyword in your class methods in Swift, the compiler will definitely yell at you because you are doing something wrong.
In swift, classes are reference type whereas structures and enumerations are value typesThe properties of value types cannot be modified within its instance methods by default. In order to modify the properties of a value type, you have to use the mutating keyword in the instance method. With this keyword, your method can then have the ability to mutate the values of the properties and write it back to the original structure when the method implementation ends.
Below is a simple implementation of Stack in Swift that illustrates the use of mutating functions.

struct Stack {
     var items = [Int]() // Empty items array
    
    mutating func push(_ item: Int) {
        items.append(item)
    }
    
    mutating func pop() -> Int? {
        if !items.isEmpty {
           return items.removeLast()
        }
        return nil
    }
}

var stack = Stack()
stack.push(4)
stack.push(78)
stack.items // [4, 78]
stack.pop()

stack.items // [4]

You can change value of items by creating instance of Stack. But without mutating keyword function can not change property.