We have a common Requesthandler.swift file which handles get, post http methods. We are using this file accross the App to have http request.
What should be the value for the delegateQueue, when we initialising the URLSession.
let session = URLSession(configuration: config, delegate: self, delegateQueue:OperationQueue.main)
The key consideration is that the queue should be a serial queue. As the docs say:
The queue should be a serial queue, in order to ensure the correct ordering of callbacks.
So, if you instantiate your own OperationQueue, make sure to set its maxConcurrentOperationCount to 1.
The docs go on to say:
If [the delegate queue is] nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.
As such, we generally we would just leave this as nil, and let URLSession take care of this for us.
One generally would not use the main queue, though. This is largely a matter of convention (as URLSession.shared, which we generally use if we don’t need delegate methods or custom behaviors, uses a serial background queue). This practice, of using a serial background queue, is advisable, as you a lower the risk that some slow parsing operation (or whatever) in a delegate method or completion handler would ever affect your main thread responsiveness. That having been said, whenever using a serial background queue, make sure to dispatch UI updates (and the like) back to the main queue.
If you use it across the app for all http that is the correct way to do it. However, I would suggest not to create your own session. There is already something exactly for that, which is URLSession.shared, albeit with (just a few) limitations as discussed in the docs.
If you can live with the limitations great - use the shared session; otherwise use nil as Rob suggested or if you DIY the queue then you do NOT want to use main as your delegate queue. You definitely want a background queue here. If you DIY then you'd probably create a singleton queue and use that for all requests and configure it according to taste (and make it serial - see Rob's answer).
Finally, since the delegate will call the completion on the delegate queue, which is NOT main, you should switch to main in the completion whenever you need to update UI, so there will probably be a lot of switching to main there. That is not wrong nor telling you to use main as queue.
Related
I'm implementing an iOS app which make requests using REST service. And I want to cancel all the previous REST calls when I make a new call. Can I do it in the global queue in GCD without Operation Queue? Thanks
GCD does not provide an API to cancel blocks. So you will have to implement this cancellation yourself.
The easiest way probably would be to set a global flag 'canceled' and check that inside of your blocks. If the flag is set, immediately return from your block. Then after all blocks have finished you can reset the flag and enqueue your new block.
Even though that sounds simple, this requires some nontrivial code. It would be much easier to use NSOperationQueue instead of reimplementing its features on top of plain GCD.
My application gets the current device location, POSTs this to my server, and returns a dictionary to be displayed in a table view. Currently I am using the CLLocationManager delegate methods and AFJSONRequestOperation (AFNetworking's retrieve-data-through-NSURLConnection-in-NSOperation class) to do the job, but this functionality is inside one view controller method and I'd like to be able to reuse this across other view controllers.
I am planning to make my own NSOperation subclass, but first I wanted to know if there are any unseen pitfalls in having NSOperations (AFJSONRequestOperation) start inside of another NSOperation. Does this work as expected or should I find a way to make a queue with dependencies among the operations?
Scheduling a NSULRL connection operation within a NSOperation will fail unless you schedule it on the main run loop. AFJSONOperation (or any subclass of AFURLOperation) will succeed because under the hood, AFNetworking operations are scheduled on their own NSOperationQueue and on a custom run loop.
So - Yes. Go for it. I use NSOperation subclasses to isolate all of my worker processes. It is much cleaner than flat out GCD in the middle of your VCs or Models.
Caveat - Since all AFNetworking operations are block based and return asynchronously, your NSOperation subclass will need to be concurrent. Apple provides a detailed description of how to build this in their docs for Concurrent NSOperation Subclasses
Optionally - you could just skip building a concurrent NSOperation subclass, and fire your network operation synchronously from within your NSOperation since you're already off of the main queue.
All the NSOperation stuff uses GCD under the hood, and GCD has no issues with nested dispatch_async calls, so I doubt nesting NSOperations will cause you problems as long as your logic is correct.
I have a streaming iOS app that captures video to Wowza servers.
It's a beast, and it's really finicky.
I'm grabbing configuration settings from a php script that shoots out JSON.
Now that I've implemented that, I've run into some strange threading issues. My app connects to the host, says its streaming, but never actually sends packets.
Getting rid of the remote configuration NSURLConnection (which I've made sure is properly formatted) delegate fixes the problem. So I'm thinking either some data is getting misconstrued across threads or something like that.
What will help me is knowing:
Are NSURLConnection delegate methods called on the main thread?
Will nonatomic data be vulnerable in a delegate method?
When dealing with a complex threaded app, what are the best practices for grabbing data from the web?
Have you looked at AFNetworking?
http://www.raywenderlich.com/30445/afnetworking-crash-course
https://github.com/AFNetworking/AFNetworking
It's quite robust and helps immensely with the threading, and there are several good tutorials.
Are NSURLConnection delegate methods called on the main thread?
Yes, on request completion it gives a call back on the main thread if you started it on the main thread.
Will nonatomic data be vulnerable in a delegate method?
Generally collection values (like array) are vulnerable with multiple threads; the rest shouldn't create anything other than a race problem.
When dealing with a complex threaded app, what are the best practices for grabbing data from the web?
I feel it's better to use GCD for handling your threads, and asynchronous retrieval using NSURLConnection should be helpful. There are few network libraries available to do the boilerplate code for you, such as AFNetworking, and ASIHTTPRequest (although that is a bit old).
Are NSURLConnection delegate methods called on the main thread?
Delegate methods can be executed on a NSOperationQueue or a thread. If you not explicitly schedule the connection, it will use the thread where it receives the start message. This can be the main thread, but it can also any other secondary thread which shall also have a run loop.
You can set the thread (indirectly) with method
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode
which sets the run loop which you retrieved from the current thread. A run loop is associated to a thread in a 1:1 relation. That is, in order to set a certain thread where the delegate methods shall be executed, you need to execute on this thread, retrieve the Run Loop from the current thread and send scheduleInRunLoop:forMode: to the connection.
Setting up a dedicated secondary thread requires, that this thread will have a Run Loop. Ensuring this is not always straight forward and requires a "hack".
Alternatively, you can use method
- (void)setDelegateQueue:(NSOperationQueue *)queue
in order to set the queue where the delegate methods will be executed. Which thread will be actually used for executing the delegates is then undetermined.
You must not use both methods - so schedule on a thread OR a queue. Please consult the documentation for more information.
Will nonatomic data be vulnerable in a delegate method?
You should always synchronize access to shared resources - even for integers. On certain multiprocessor systems it is not even guaranteed that accesses to a shared integer is safe. You will have to use memory barriers on both threads in order to guarantee that.
You might utilize serial queues (either NSOperationQueue or dispatch queue) to guarantee safe access to shared resources.
When dealing with a complex threaded app, what are the best practices for grabbing data from the web?
Utilize queues, as mentioned, then you don't have to deal with threads. "Grabbing data" is not only a threading problem ;)
If you prefer a more specific answer you would need to describe your problem in more detail.
To answer your first question: The delegate methods are called on the thread that started the asynchronous load operation for the associated NSURLConnection object.
I am doing a lot of GCD and asynchronous rendering and data retrieval work lately and I really need to nail the mental model about how asynchronous is done.
I want to focus on setNeedsDisplay and the NSURLConnectionDelegate suite of methods.
Is it correct to call setNeedsDisplay asynchronous? I often call it via dispatch_async(dispatch_get_main_queue(), ^{}) which confuses me.
The NSURLConnectionDelegate callbacks are described as asynchronous but are they not actually concurrently run on the main thread/runloop. I am a but fuzzy on the distinction here.
More generally in the modern iOS era of GCD what is the best practice for making GCD and these methods play nice together. I'm just looking for general guidelines here since I use them regularly and am just trying not to get myself in trouble.
Cheers,
Doug
No, you generally don't call setNeedsDisplay asynchronously. But if you're invoking this from a queue other than the main queue (which I would guess you are), then you should note that you never should do UI updates from background queues. You always run those from the main queue. So, this looks like the very typical pattern of dispatching a UI update from a background queue to the main queue.
NSURLConnection is described as asynchronous because when you invoke it, unless you used sendSynchronousRequest, your app immediately returns while the connection progresses. The fact that the delegate events are on the main queue is not incompatible with the notion that the connection, itself, is asynchronous. Personally, I would have thought it bad form if I can some delegate methods that were not being called from the same queue from which the process was initiated, unless that was fairly explicit via the interface.
To the question of your question's title, whether NSURLConnection uses GCD internally, versus another concurrency technology (NSOperationQueue, threads, etc.), that's an internal implementation issue that we, as application developers, don't generally worry about.
To your final, follow-up question regarding guidelines, I'd volunteer the general rule I alluded to above. Namely, all time consuming processes that would block your user interface should be dispatched to background queue, but any subsequent UI updates required by the background queue should be dispatched back to the main queue. That's the most general rule of thumb I can think of that encapsulates why we generally do concurrent programming and how to do so properly.
What is the differance between adding a operation which make a synchronous NSURLConnection request in NSOperationQueue ( or synchronous request from a thread ( not main thread)) AND making a asynchronous request from the main thread ?
Both will not block main thread so UI will remain responsive but is there any advantage of using one over other? I know in later method i can track request progress etc but assume that progress and other HTTP stuff is not important here.
They are very similar. The biggest problem with synchronous requests is that they can't easily be cancelled. Depending on your application, that could be a problem. Imagine you are downloading a big document and the user moves to another screen so you no longer need that information. In our case, I actually chose doing asynchronous NSURLConnections on a secondary NSThread, which may be overkill for some apps. It is more complicated, but it gives us the ability to both cancel requests and to decode the JSON/XML/image data on secondary threads so they don't impact main thread user interactivity.
Asynchronous requests are scheduled on the run loop and setup as a run loop source, triggering the code automatically only when there is data received from the network (as any socket source).
Synchronous requests running on a NSThread monopolizes a thread to monitor the incoming data, which is in general quite overkill.
You can always cancel an NSURLConnection even if it has been executed asynchronously, using the cancel method.
I bet using the new API that allows to send an asynchronous request on an NSOperationQueue (+sendAsynchronousRequest:queue:completionHandler:) uses GCD under the hood and dispatch_source_create, or something similar, so that it behave the same way as when an NSURLConnection is scheduled on the run loop, avoiding using an additional thread (watch the WWDC'12 videos that explains why threads are evil and their usage should be minimized), the difference only being that allows you to use a block to be informed upon completion instead of using the delegate mechanism.
Some years ago I created a class that embedded NSURLConnection asynchronous calls and delegate management into a nice block API (see OHURLLoader on my github) that makes it easier to use (feel free to take a look). I bet the new API that uses NSOperationQueues uses the same principle, still doing asynchronous requests on the runloop but allowing you to use blocks instead of having to implement a delegate.
The historical position was that there's an advantage in power consumption, and therefore battery life, in asynchronous requests — presumably including both the older delegate approach and the new block-based approach.