I have a swift class that does runs a multi-step process, much like this question has: Simple GCD Serial Queue example like FIFO using blocks
So i have a single public method of func start(), then a bunch of private methods which contain each "step" of the process.
I've spoken to a few people about this and i've been told i don't need to use a load of completion handlers and can instead use a serial dispatch queue.
However, if some of those steps run something that contains its own queue, then the method will end (and the next step starts) before it really has completed. So for example if func step1() uses Alamofire to download some data from an API, and step2() turns the response json into data models, step1() will return before the network response comes in. This is why i originally used callbacks, however i'm being told i don't need them?
The only way i can see a serial queue working is if there was a way of manually telling the queue when a certain task is complete (which there isn't?).
Could anyone help me work out the best way to structure and run this multi-step process, preferably without a load of nested completion handlers?
Or in other words: How do you use serial dispatch queues if some of the tasks you push to the queue will do things like network requests that return immediately.
You can use NSOperationQueue for that. NSOperationQueue is built on top of GCD and specially designed to manage a number of dependent jobs.
Related
I'm working on a game that I'm wanting to add cloud saves via GameKit. The original save code was based on synchronous file I/O and blocks the main queue. Moving away from this design would be a massive amount of work. Unfortunately, it seems like the GameKit APIs dispatch callbacks on the main queue which causes a deadlock in this case.
Given this, is there a way to manually process the blocks in a dispatch queue? That way the blocking code could process the main queue while waiting for the callbacks, eliminating the deadlock.
You can setup a chain of responsibility using Operations. Setup the dependencies between each operation, and then drop them in their respective queues using Grand Central Dispatch.
UI code should be in main and there’s Quality Of Services for background tasks and USER generated activity. You can create a dispatch queues and set them to be synchronous (serial) or asynchronous (parallel).
If you include code, I will post examples. Otherwise there’s several options on how to do the above.
An app I am working on requires creating a container object on a server and inserting items into that container. I don't want to create the container object until the first item needs to be inserted. However, creating the container object requires some initialization that may take a little time. While that container is still initializing the user can still to send insertion requests that aren't getting handled because the container isn't ready yet. I have two main questions:
Should this be dealt with on the client or server side?
What is the best practice for dealing with kind of this issue?
Essentially, I need to ensure my initial createContainer data task in complete before any insertItem requests are sent.
Addition Information
An insertItem request is sent by clicking on a corresponding tableViewCell. The first tableViewCell a user clicks on sends a createContainer request that creates a container holding the first item.
For a container holding n items, the request should be sent in the following order:
createContainer(Container(with: item1)
insertItem(item2)
...
insertItem(itemn)
After the first request completes, the remaining n – 1 requests may complete in any order.
My Thoughts
It sounds like I want the createContainer request to be handled synchronously while the insertItem request should be handled asynchronously. I'm not sure if that is the best approach or even how to perform that appropriately, so any guidance would be greatly appreciated.
You can use a NSOperationQueue and multiple NSOperations to implement your desired behavior. A NSOperation instance can be dependent on the completion of another NSOperation instance:
dependencies
An array of the operation objects that must finish
executing before the current object can begin executing.
For your example this would mean that the insertItem-Operations are dependent on the createContainer operation.
When you add all those operations to a NSOperationQueue your createContainer operation will run first. When it has finished, the other operations will start running as their dependencies are now satisfied. You can also control how many operations you want to run concurrently using maxConcurrentOperationCount on NSOperationQueue.
As you will be using asynchronous API in your NSOperations you will need to implement a ConcurrentOperation and handle the state changes yourself. The API Reference is explaining this in pretty good detail.
Check out the API Reference for NSOperation for further information.
There is also a nice NSHipster article on NSOperations.
Adding to the NSOperationQueue answer, it's sometimes difficult to manually manage all the state changes that an NSOperation requires to handle something asynchronous like a network call.
To simplify that, you can use a Swift Library called Overdrive. It's an amazing library in which you simply subclass a Task class and write your network code in the run() function. And when you're done, you simply call self.finish to finish the task. Here's an example: Just create a simple download task:
Then, just add it to the queue.
You can also add dependencies between tasks, which basically solves your use case.
Hope this helps.
The question is if an app should have one instance of the queue for all async operations, or several queues can be created?
With one queue it's pretty simple because all tasks are executed based on assigned priority. So for me it's more favourable because there is no need to write extra code.
In the case of multiple queues at least one of them should be main.
So some sort of queue manager should be implemented that will be able to suspend "sub" queues and allow execution of operations from main queue if needed.
The analogy with only one single connection to database make me think that the one centralised queue should be used for all async operations.
So what would you recommend? What are the best practices?
After doing some searching and brainstorming I came up to the solution.
In my application I'm going to use 2 async queues:
one queue (NSOperationQueue) for all background operations (like parsing of downloaded .json files) and another queue (which is already implemented by NSURLSession class) for requests (API requests, downloading images, etc).
NSURLConnection.sendAsynchronousRequest
and
dispatch_async(dispatch_get_main_queue()) {}
I see that one is specific to urlRequests but could one have also used the dispatch_async function to get a data from URL then do UI related stuff in an asynchoronous fashion?
Thanks in advance,
Ace
Like you said the NSURLConnection method is specifically for sending async request and acts at a higher level of abstraction. Meaning a lot of heavy lifting is done for you under the hood.
Also what you do in the example is dispatching the call of the block you would supply asynchronously, but the block itself would be executed on the main queue, which would not be asynchronous.
You could for example download something in the background with the asynchronous request and then do UI related stuff on the main queue with your dispatch_async call.
So to speak: dispatch_async is part of the rather low-level GCD framework that can be used for a variety of things, like dispatching arbitrary codeblocks on different queues etc. See here for reference
I am using GCD on iOS to perform a a time-consuming task on a background thread. The API has a start method that takes two blocks as arguments, both called on the main queue. The first is called when the task starts and the second when the task finishes. This all works.
I actually need to do several of these time-consuming tasks. The API lets me start them all at the same time and then wait for each to finish and update the UI via the blocks. They run concurrently.
However what I actually I want to do is to sequence the time-consuming tasks (still starting each using the API described) so that I can start them all at the same time, have the first one run and give me its call-backs, then have the second one run and give me its call-backs, etc. until all are done.
What is the best way to achieve this with GCD and blocks?
If the tasks were synchronous, I'd just have a loop that ran each in turn, and run all of that asynchronously. But I have call-backs, so that will not work. I'd prefer not to have to chain them, since the object that makes all of this happen could disappear once it has started the sequence of events.
You can create your own serial queue that will execute in FIFO order with dispatch_queue_create. You DO NOT need to specify that it is a serial queue. It will act this way by default.
Sample queue creation:
dispatch_queue_t my_q = dispatch_queue_create("Serial",NULL);
You own this queue, so failing to release it (with dispatch_release) will leak it.
More info is in Apple's docs here.
Is there a particular reason you have to use GCD? Sounds like NSOperationQueue with concurrency of 1 is exactly what you want.