Jacob is studying on programming 프로그래밍 관련정보를 모아둔 jacob yoo 의 개인 블로그입니다
  • Home
  • programming
  • About me
  • contact me

Daily Archives: April 22, 2018

You are browsing the site archives by date.
22Apr 2018

by jacob ⋅ Link

original source : https://agostini.tech/2017/07/30/understanding-operation-and-operationqueue-in-swift/#comment-141

There are more ways than one to do concurrent operations on iOS. In this article, we’ll cover how to use Operations and OperationQueue to execute concurrent operations. There’s a lot to be said (and written) about concurrency on iOS and I don’t really want to bore you to death by writing a massive article about it, so I’ll focus here on Operations because chances are these will cover most of your needs for concurrent operations. We’ll touch a bit on GCD because sometimes you just need to dig in deep and do it yourself.

Concurrency

This is just a fancy way of saying ‘doing stuff at the same time’. The iPhone is capable of executing multiple threads at the same time. I could go into the nitty gritty details about threads and queues, but you don’t really care about them. You just want to use your OperationQueue… Fair enough

Just remember one thing – on your iOS device you’ll see two types of queues; the main queue, and everything else. In your day-to-day development, you can split all of your operations as being run on the main queue or off the main queue. Clearly, the main queue is important… So what is it exactly? Well, it’s where your UI gets updated. All the user interactions and all the animations get executed on the main queue. You don’t really want to run your code on the main queue because you’ll be blocking the UI and the user will see the UI as being unresponsive so they might even kill your app.

The Queues

All your UI updates should be performed on the main queue and you should do as much work as possible in the background, which is sometimes referred to as ‘off the main queue’. You want to keep the main queue clear.

The main queue is like a priority boarding queue at an airport. Some people buy priority boarding but most don’t and the people in the priority boarding queue board the plane before the people in the other queue (at least that’s how Ryanair does it ) Imagine what would happen if everyone bought a priority boarding pass. That queue would be full and the other queue would be empty so the priority queue would lose its purpose. The priority queue, just like the main queue, works only if you use it sparingly. There can be only one main queue and you can have as many background queues as you want.

The most common example would be refreshing a table view. You pull to refresh, start animating the spinner, kick off your networking request in the background and then when you’re finished processing and parsing the response you reload the table on the main queue.

Let’s use this example to demonstrate the usage of Operations and OperationQueue. I’ll use one of my projects on GitHub – DADependencyInjection – to demonstrate this.

Operation

Operation is an abstract class and represents a logical unit of work. For example, in your code you probably have a method that fetches data from the API. If you want to use OperationQueue you would have to create your class that subclasses Operation and move that method in your new class. Doing this has an added benefit of forcing you to use the single responsibility principle.

Once you have your class you add it to your OperationQueue and wait for it to finish executing. 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.

In our example we’ll use two operations, but first, we’ll create a base class for our operations just to get some of the clutter out of the way:

class DAOperation: Operation {

   private var _executing = false {
       willSet {
           willChangeValue(forKey: “isExecuting”)
       }
       didSet {
           didChangeValue(forKey: “isExecuting”)
       }
   }

   override var isExecuting: Bool {
       return _executing
   }

   private var _finished = false {
       willSet {
           willChangeValue(forKey: “isFinished”)
       }

       didSet {
           didChangeValue(forKey: “isFinished”)
       }
   }

   override var isFinished: Bool {
       return _finished
   }

   func executing(_ executing: Bool) {
       _executing = executing
   }

   func finish(_ finished: Bool) {
       _finished = finished
   }
}

We’ll create two operations: one for networking and one for parsing. Our networking operation will look like this:

class GetDataOperation: DAOperation {

   private let urlString: String
   private let provider: NetworkingProvider

   var responseData: Data?

   init(withURLString urlString: String, andNetworkingProvider provider: NetworkingProvider = AFNetworkConnector()) {
       self.urlString = urlString
       self.provider = provider
   }

   override func main() {
       guard isCancelled == false else {
           finish(true)
           return
       }

       executing(true)
       provider.restCall(urlString: urlString) { (data) in
           self.responseData = data
           self.executing(false)
           self.finish(true)
       }
   }
}

We’re initializing this class with a URL string and a networking provider. We’re exposing a ‘responseData’ property where we’ll save the networking response. This class is nothing more than a wrapper around our networking provider. Parse operation is even simpler:

Parse operation is even simpler:

class ParseDataOperation: DAOperation {

   private let factory: MoviesFactoryProvider

   var moviesData: Data?
   var movies: [MovieItem]?

   init(withFactory factory: MoviesFactoryProvider = MoviesFactory()) {
       self.factory = factory
   }

   override func main() {
       guard isCancelled == false else {
           finish(true)
           return
       }

       executing(true)
       guard
           let data = moviesData,
           let jsonObject = try? JSONSerialization.jsonObject(with: data, options: [JSONSerialization.ReadingOptions.allowFragments])
           else {
               executing(false)
               finish(true)
               return
       }

       movies = factory.movieItems(withJSON: jsonObject)

       executing(false)
       finish(true)
   }
}

We have two important properties at the top: ‘moviesData’ and ‘movies’. Networking operation will set the ‘moviesData’ property. We’ll feed this data into our factory and save the movies array into the instance variable ‘movies’.

When you start your operation the main method gets called. This method will be called by the OperationQueue automatically so all you have to do is add your operations to the queue.

OperationQueue

You create your OperationQueue and start adding your operations to it. OperationQueue is a prioritised FIFO queue which means you can set priority on individual operations. Those with the highest priority get pushed ahead, but not necessarily to the front of the queue, as iOS determines when to actually execute the operation. Operations with the same priority get executed in the order they were added to the queue unless an operation has dependencies.

You can set the priority of your operations by setting your queuePriority property of the operation. You can also control how much of the system resources will be given to your operation by setting qualityOfService property. OperationQueue can execute multiple operations at the same time. You can set the number of maxConcurrentOperationCount or you can let the system set this property to the default value which is different for each device.

A beautiful thing about operations is that they can be cancelled which is something you can’t do with blocks. Let’s say you have three operations: networking call, parsing of data and saving in the database. If your networking call fails, there’s little sense in executing the remaining two operations so you can simply cancel them.

In our example we’ll create a new data provider for movies. We’ll create a new property, ‘operationQueue’, and initialize it to the OperationQueue:

private let operationQueue: OperationQueue = OperationQueue()

Now all we need to do is create our operations and add them to the queue:

let networkingOperation = GetDataOperation(withURLString: urlString, andNetworkingProvider: networkingProvider)
let parsingOperation = ParseDataOperation(withFactory: moviesFactory)

operationQueue.addOperations([networkingOperation, parsingOperation], waitUntilFinished: false)

As soon as you add your operations to the queue they will start executing. As I said before the main method will get called for each operation when it starts executing.

Dependencies

In some cases your operations might depend on other operations to finish.

Your ParseDataOperation depends on the successful completion of the GetDataOperation. If your operation has dependencies it will not start until they all finish. This little feature is really powerful if you’re dealing with complex logic because it greatly simplifies your code.

Let’s go over this example for dependencies:

networkingOperation.completionBlock = {
  parsingOperation.moviesData = networkingOperation.responseData
}

parsingOperation.completionBlock = {
  onCompleted?(parsingOperation.movies ?? [])
}
parsingOperation.addDependency(networkingOperation)

We have our two operations again: parsingOperation and networkingOperation, but this time parsingOperation has a dependency set to networkingOperation. So parsingOperation will never start before networkingOperation.

Every operation has a ‘completionBlock’ that gets called when the operation finishes. We’ll use these completion blocks to pass data between operations. When the networking operation completes it sets the ‘moviesData’ property on the parsing operation. And when the parsing operation completes it calls the closure and passes in the movies.

At the end of the code listing, we’re setting our dependency.

What About GCD?

GCD stands for Grand Central Dispatch. You might think that with OperationQueues and Operations there’s no need for GCD but they actually work together. Fun fact: Operations are built on top of GCD.

I primarily use GCD to dispatch my closures back on the main thread. We talked a lot about Operations and OperationQueues here, but sooner or later you will have to update your UI to reflect the state of your app. UI updates must happen on the main thread so you need a way to dispatch code back to the main thread. One of the easiest ways to do that is to use GCD.

GCD is great if you need to use advanced things like semaphores and barriers. I won’t go into details on how to use them here but you can see an example of using barriers in my article on Singletons in Swift.

The most common use of GCD is to run a piece of code on the main thread – like the ‘onCompleted’ closure in our example here:

DispatchQueue.main.async(execute: {
  onCompleted?(listItems)
})

Conclusion

OperationQueue is a great tool to use if you have complex logic in your app and if you have to manage dependencies between the operations. It’s very elegant at that. Passing data between operations is a bit messy. When your operation is finished you store the data in an instance variable which you read from the dependent operation. That’s not very elegant at all. There are much neater solutions out there like, for example, PromiseKit, which I’ll cover in one of the next articles.

I hope I gave you enough of an intro into Operations so you can use them in your apps where appropriate. You can find all the example code in my GitHub repo.

Have a nice day

Dejan.

Share this:

  • Click to print (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on Tumblr (Opens in new window)
  • Click to share on Twitter (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Google+ (Opens in new window)
22Apr 2018

by jacob ⋅ Link

original source : https://agostini.tech/2017/07/30/understanding-operation-and-operationqueue-in-swift/#comment-141

There are more ways than one to do concurrent operations on iOS. In this article, we’ll cover how to use Operations and OperationQueue to execute concurrent operations. There’s a lot to be said (and written) about concurrency on iOS and I don’t really want to bore you to death by writing a massive article about it, so I’ll focus here on Operations because chances are these will cover most of your needs for concurrent operations. We’ll touch a bit on GCD because sometimes you just need to dig in deep and do it yourself.

Concurrency

This is just a fancy way of saying ‘doing stuff at the same time’. The iPhone is capable of executing multiple threads at the same time. I could go into the nitty gritty details about threads and queues, but you don’t really care about them. You just want to use your OperationQueue… Fair enough

Just remember one thing – on your iOS device you’ll see two types of queues; the main queue, and everything else. In your day-to-day development, you can split all of your operations as being run on the main queue or off the main queue. Clearly, the main queue is important… So what is it exactly? Well, it’s where your UI gets updated. All the user interactions and all the animations get executed on the main queue. You don’t really want to run your code on the main queue because you’ll be blocking the UI and the user will see the UI as being unresponsive so they might even kill your app.

The Queues

All your UI updates should be performed on the main queue and you should do as much work as possible in the background, which is sometimes referred to as ‘off the main queue’. You want to keep the main queue clear.

The main queue is like a priority boarding queue at an airport. Some people buy priority boarding but most don’t and the people in the priority boarding queue board the plane before the people in the other queue (at least that’s how Ryanair does it ) Imagine what would happen if everyone bought a priority boarding pass. That queue would be full and the other queue would be empty so the priority queue would lose its purpose. The priority queue, just like the main queue, works only if you use it sparingly. There can be only one main queue and you can have as many background queues as you want.

The most common example would be refreshing a table view. You pull to refresh, start animating the spinner, kick off your networking request in the background and then when you’re finished processing and parsing the response you reload the table on the main queue.

Let’s use this example to demonstrate the usage of Operations and OperationQueue. I’ll use one of my projects on GitHub – DADependencyInjection – to demonstrate this.

Operation

Operation is an abstract class and represents a logical unit of work. For example, in your code you probably have a method that fetches data from the API. If you want to use OperationQueue you would have to create your class that subclasses Operation and move that method in your new class. Doing this has an added benefit of forcing you to use the single responsibility principle.

Once you have your class you add it to your OperationQueue and wait for it to finish executing. 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.

In our example we’ll use two operations, but first, we’ll create a base class for our operations just to get some of the clutter out of the way:

class DAOperation: Operation {

   private var _executing = false {
       willSet {
           willChangeValue(forKey: “isExecuting”)
       }
       didSet {
           didChangeValue(forKey: “isExecuting”)
       }
   }

   override var isExecuting: Bool {
       return _executing
   }

   private var _finished = false {
       willSet {
           willChangeValue(forKey: “isFinished”)
       }

       didSet {
           didChangeValue(forKey: “isFinished”)
       }
   }

   override var isFinished: Bool {
       return _finished
   }

   func executing(_ executing: Bool) {
       _executing = executing
   }

   func finish(_ finished: Bool) {
       _finished = finished
   }
}

We’ll create two operations: one for networking and one for parsing. Our networking operation will look like this:

class GetDataOperation: DAOperation {

   private let urlString: String
   private let provider: NetworkingProvider

   var responseData: Data?

   init(withURLString urlString: String, andNetworkingProvider provider: NetworkingProvider = AFNetworkConnector()) {
       self.urlString = urlString
       self.provider = provider
   }

   override func main() {
       guard isCancelled == false else {
           finish(true)
           return
       }

       executing(true)
       provider.restCall(urlString: urlString) { (data) in
           self.responseData = data
           self.executing(false)
           self.finish(true)
       }
   }
}

We’re initializing this class with a URL string and a networking provider. We’re exposing a ‘responseData’ property where we’ll save the networking response. This class is nothing more than a wrapper around our networking provider. Parse operation is even simpler:

Parse operation is even simpler:

class ParseDataOperation: DAOperation {

   private let factory: MoviesFactoryProvider

   var moviesData: Data?
   var movies: [MovieItem]?

   init(withFactory factory: MoviesFactoryProvider = MoviesFactory()) {
       self.factory = factory
   }

   override func main() {
       guard isCancelled == false else {
           finish(true)
           return
       }

       executing(true)
       guard
           let data = moviesData,
           let jsonObject = try? JSONSerialization.jsonObject(with: data, options: [JSONSerialization.ReadingOptions.allowFragments])
           else {
               executing(false)
               finish(true)
               return
       }

       movies = factory.movieItems(withJSON: jsonObject)

       executing(false)
       finish(true)
   }
}

We have two important properties at the top: ‘moviesData’ and ‘movies’. Networking operation will set the ‘moviesData’ property. We’ll feed this data into our factory and save the movies array into the instance variable ‘movies’.

When you start your operation the main method gets called. This method will be called by the OperationQueue automatically so all you have to do is add your operations to the queue.

OperationQueue

You create your OperationQueue and start adding your operations to it. OperationQueue is a prioritised FIFO queue which means you can set priority on individual operations. Those with the highest priority get pushed ahead, but not necessarily to the front of the queue, as iOS determines when to actually execute the operation. Operations with the same priority get executed in the order they were added to the queue unless an operation has dependencies.

You can set the priority of your operations by setting your queuePriority property of the operation. You can also control how much of the system resources will be given to your operation by setting qualityOfService property. OperationQueue can execute multiple operations at the same time. You can set the number of maxConcurrentOperationCount or you can let the system set this property to the default value which is different for each device.

A beautiful thing about operations is that they can be cancelled which is something you can’t do with blocks. Let’s say you have three operations: networking call, parsing of data and saving in the database. If your networking call fails, there’s little sense in executing the remaining two operations so you can simply cancel them.

In our example we’ll create a new data provider for movies. We’ll create a new property, ‘operationQueue’, and initialize it to the OperationQueue:

private let operationQueue: OperationQueue = OperationQueue()

Now all we need to do is create our operations and add them to the queue:

let networkingOperation = GetDataOperation(withURLString: urlString, andNetworkingProvider: networkingProvider)
let parsingOperation = ParseDataOperation(withFactory: moviesFactory)

operationQueue.addOperations([networkingOperation, parsingOperation], waitUntilFinished: false)

As soon as you add your operations to the queue they will start executing. As I said before the main method will get called for each operation when it starts executing.

Dependencies

In some cases your operations might depend on other operations to finish.

Your ParseDataOperation depends on the successful completion of the GetDataOperation. If your operation has dependencies it will not start until they all finish. This little feature is really powerful if you’re dealing with complex logic because it greatly simplifies your code.

Let’s go over this example for dependencies:

networkingOperation.completionBlock = {
  parsingOperation.moviesData = networkingOperation.responseData
}

parsingOperation.completionBlock = {
  onCompleted?(parsingOperation.movies ?? [])
}
parsingOperation.addDependency(networkingOperation)

We have our two operations again: parsingOperation and networkingOperation, but this time parsingOperation has a dependency set to networkingOperation. So parsingOperation will never start before networkingOperation.

Every operation has a ‘completionBlock’ that gets called when the operation finishes. We’ll use these completion blocks to pass data between operations. When the networking operation completes it sets the ‘moviesData’ property on the parsing operation. And when the parsing operation completes it calls the closure and passes in the movies.

At the end of the code listing, we’re setting our dependency.

What About GCD?

GCD stands for Grand Central Dispatch. You might think that with OperationQueues and Operations there’s no need for GCD but they actually work together. Fun fact: Operations are built on top of GCD.

I primarily use GCD to dispatch my closures back on the main thread. We talked a lot about Operations and OperationQueues here, but sooner or later you will have to update your UI to reflect the state of your app. UI updates must happen on the main thread so you need a way to dispatch code back to the main thread. One of the easiest ways to do that is to use GCD.

GCD is great if you need to use advanced things like semaphores and barriers. I won’t go into details on how to use them here but you can see an example of using barriers in my article on Singletons in Swift.

The most common use of GCD is to run a piece of code on the main thread – like the ‘onCompleted’ closure in our example here:

DispatchQueue.main.async(execute: {
  onCompleted?(listItems)
})

Conclusion

OperationQueue is a great tool to use if you have complex logic in your app and if you have to manage dependencies between the operations. It’s very elegant at that. Passing data between operations is a bit messy. When your operation is finished you store the data in an instance variable which you read from the dependent operation. That’s not very elegant at all. There are much neater solutions out there like, for example, PromiseKit, which I’ll cover in one of the next articles.

I hope I gave you enough of an intro into Operations so you can use them in your apps where appropriate. You can find all the example code in my GitHub repo.

Have a nice day

Dejan.

Share this:

  • Click to print (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on Tumblr (Opens in new window)
  • Click to share on Twitter (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Google+ (Opens in new window)

Recent Posts

  • android 3rd party animation libraries recommended by tacademy
  • android animation 정리
  • android 중급 2강 Thread(스레드)2  tacademy
  • rfid project
  • android 중급 1강 Thread(스레드)1  tacademy

Recent Comments

    Archives

    • December 2019
    • November 2019
    • October 2019
    • September 2019
    • August 2019
    • July 2019
    • June 2019
    • May 2019
    • April 2019
    • March 2019
    • February 2019
    • January 2019
    • December 2018
    • November 2018
    • October 2018
    • September 2018
    • August 2018
    • July 2018
    • June 2018
    • May 2018
    • April 2018
    • March 2018
    • February 2018
    • January 2018
    • December 2017
    • November 2017
    • October 2017
    • September 2017
    • August 2017
    • July 2017
    • June 2017
    • May 2017
    • April 2017
    • March 2017
    • February 2017
    • January 2017
    • December 2016
    • November 2016
    • October 2016
    • September 2016
    • August 2016
    • July 2016
    • June 2016
    • May 2016
    • April 2016
    • March 2016
    • February 2016
    • January 2016
    • December 2015
    • November 2015
    • October 2015
    • August 2015
    • July 2015
    • June 2015
    • May 2015
    • April 2015
    • March 2015
    • February 2015
    • January 2015
    • December 2014
    • November 2014
    • October 2014
    • September 2014
    • August 2014
    • July 2014
    • June 2014
    • May 2014
    • April 2014
    • March 2014
    • February 2014
    • January 2014
    • December 2013
    • November 2013
    • October 2013
    • September 2013
    • August 2013
    • July 2013
    • June 2013
    • May 2013
    • April 2013
    • March 2013
    • February 2013
    • January 2013
    • December 2012
    • November 2012
    • October 2012
    • September 2012

    공부했던 내용

    android api app brian class core core data css data database development django doc documentation drupal facebook firebase function google http image ios jacob java javascript jquery laravel layout library machine learning mysql notification php plugin python swift type watch wear wearable web 개념 개발 설명 자바스크립트
    April 2018
    M T W T F S S
    « Mar   May »
     1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30  
    © Copyright 2022 - Jacob is studying on programming
    Contango Theme ⋅ Powered by WordPress