I want to have different priorities for different http requests and I want to be able to stop/pause some requests immediately and to execute only some of them if I have a request with High priority. I was going to use different approaches, but it seems that all of them are deprecated now:
1) To use AFHTTPRequestOperation, so I can create different OperationQueue base on DispatchQueue and add operations there, but it's deprecated in a new version of AFNetworking
2) To use different DispatchQueues with different priorities for different Synchronous requests (using NSURLConnection.sendSynchronousRequest) (so I can stop some queues if I have highest priority operations and I can cancel operations immediately). But as far as I understand according to Stackoverflow Question , this way is deprecated, so I can't send a Synchronous request.
I understand that there are ways where Requests seem to be Synchronous using semaphores, but that's only an illusion, because all the requests will be executed in the queues I can't control.
Are there any ways to control the DispatchQueue (or OperationQueue) where the request is executed?
You should/could use NSURLSessionTask/URLSessionTask subclasses to grab data from the network. You could easily encapsulate them inside one NSOperation/Operation subclasses (see this slide.
You would just need to add cancellation support to it. You can find information about cancellation inside the Responding to the Cancel Command section of the NSOperation documentation.
To play with priorities you have several tools:
To handle priorities between OperationQueue instances, you can attribute them different qualityOfService values.
To handle priorities between Operation instances, you can attribute them different qualityOfService, queuePriority or threadPriority values.
Some additional explanation about those parameters can be found in the Prioritize Work with Quality of Service Classes section of the Energy Efficiency Guide for iOS Apps.
Related
I have two questions about multi-threads in iOS
if I want to simulate 100 concurrent API call, which means these 100 API calls start at the same time, how should I do it?
Like if something like this
for i in 0..<100 {
//Start API call
}
or add 100 operations in one operationQueue and set max concurrent over 100, start the operations in the queue
all these should means start the API call one by one, so how to start them at the same time? just like add 100 operations and start together
How to add and monitor NSURLSessionDataTask objects in operationQueue? Like use waitUntilAllOperationsDone() method for multiple NSURLSessioNDataTask Objects.
I'm using something like
dispatch_group_enter(group)
session.dataTaskWithCompletion({
dispatch_group_leave(group)
})
dispatch_group_notify()
I'm wondering if this could be implemented in NSOperationQeue, it seems every thread created by NSURLSessionDataTask is randomly created by system so how to monitor it in NSOperationQueue?
Whether you start these 100 tasks by just calling some asynchronous method or using an operation queue, the effect is similar, that they won't technically all start at exactly the same time, but they should (with a few caveats) start closely enough to each other such that, assuming the asynchronous API call doesn't return instantaneously, you'll certainly have them running at the same time.
Note, if the API is using NSURLSession, you may have to adjust the httpMaximumConnectionsPerHost of your NSURLSessionConfiguration. Usually it's constrained to some reasonable value, and if you want 100 concurrent tasks (more than you'd ever want to use in a production environment), you may have to adjust this setting.
You ask a few questions about operation queues. There's little benefit, IMHO, in introducing operation queues into this discussion. In fact, one of the main use cases for operation queues is to achieve the precise opposite, when you don't want them all running concurrently, but rather want to constrain the concurrency to something reasonable (e.g. 4 at a time). So operation queues might be part of your eventual production solution, but don't introduce it solely for the sake of trying to run a lot of requests at the same time.
Also note, when trying to run a bunch of asynchronous tasks on operation queue, you can't just addOperationWithBlock or add an NSBlockOperation. You have to make an asynchronous custom subclass of NSOperation that sets the isAsynchronous flag and does the necessary KVO of isFinished and isExecuting. It's not hard, but it's enough work that it's not something I'd introduce without a compelling need.
Regarding dispatch group "notify" vs operation queue, yes, you can achieve something similar using operation queues. You can just create some "completion" operation which is dependent upon all of the other operations finishing and add that to some queue after all of the other operations have been queued.
Refer this video from WWDC https://developer.apple.com/videos/play/wwdc2015/226/
The speaker shows that we can add dependency between two NSopeation instances of same type. Example an NSoperation that displays an alert. By achieving this we can make sure that we don't throw multiple alerts at same time and annoy the user.
If one alert is already being displayed next one will wait.
I still can't figure out how to implement this dependency of NSOperations cross queue.In more simpler words can anyone show an example(implementation) of following two things.
1.Implementation of adding dependency of operation B from queue 2 on operation A from queue 1.
2.Implementation of adding dependency of multiple instances of same NSOperation type, even if they are in different queue. Example: if i add multiple instances of "AlertOperation" to different queue I want to make sure they still take place sequentially among themselves.
I would appreciate if the examples are in Objective C.
Please ask for more clarification if needed.
I'm the engineer who presented that session.
The short answer is that in order to make your second operation dependent on the first operation, you have to maintain a reference to the first operation.
The sample code provided with the session uses a global table that keeps track of all the currently-executing operations. When a new operation comes in that specifies it should be mutually exclusive with other operations of the same kind, the code looks up in the table for the other operations of the same kind. The new operation is then made dependent on the last one in the list.
Since the table is a global table, it works regardless of which queue the operations are actually executing on. The only thing it requires is using the custom NSOperationQueue subclass ("OperationQueue") as the thing that's executing operations.
From the comments, the underlying question is:
how can I add a dependency to an existing operation when I don't have a reference to it
You should create multiple different queues, and specifically in this case a queue just for alert operations. Technically it can work with a single queue, but you need to do a bit more work.
With a specific queue you can simply iterate the operations currently on the queue and add a dependency to every one. If you don't have a specific queue then you'll need to do a class test (or use some other logic) to decide exactly which operations to add a dependency to.
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).
I am making an app for website. I use JSON to get data. I want to load all posts in threads (1 post - 1 thread). How many threads I can make? Should I control the number of threads?
With Cocoa you usually don't work with Threads directly. Grand Central Dispatch (GCD) is an API that handles this for your. You just have to partition your task into small managable chunks, dispatch them onto a background queue and the rest is handled for you. You don't need to worry about creating threads, how many are currently running etc. When you dispatch enough work on one (or possibly more) queues, the CPU is going to run at maximum load.
You can also use NSOperationQueue, which has the ability to throttle the execution to some extend or cancel currently running tasks (not possible with GCD).
Unless you are doing anything unusual there is no need to use NSThread directly. Use GCD when you just need to perform a simple small task asynchronously. Use NSOperationQueue when you need more control, like cancelling submitted tasks or setting priorities. It's API is also a bit higher level and in Objective-C. GCD is a C level API, so for example it can't catch ObjC-Exceptions. NSOperationQueue uses GCD internally, so both should work equally well.
I am confused on where to use which multithreading tool in iOS for hitting services and changing UI based on service data,
firstly I got accustomed to using NSURLConnection and its delegates, used didreceiveresponse, didreceivedata etc delegates to achieve the task
secondly I learned and used GCD to hit services and update the UI from within the block code
Now I am learning to use performSelectorInBackground() to do work in background thread
Clearly confused on which tool to use where?
NSURLConnection with delegate calls is "old school" way of receiving data from remote server. Also it's not really comfortable to use with few NSURLConnection instances in a single class (UIViewController or what not). Now it's better to use sendAsynchronousRequest.. method with completion handler. You can also define on which operation queue (main for UI, or other, background one) the completion handler will be run.
GCD is good for different tasks, not only fetching remote resources with initWithContentsOfURL: methods. You can also control what type of queues will receive your blocks (concurrent, serial ,etc.)
performSelectorInBackground: is also "old school" way of performing a method in background thread. If you weren't using ARC, you'd need to setup separate autorelease pool to avoid memory leaks. It also has a limitation of not allowing to accept arbitrary number of parameters to given selector. In this case it's recommended to use dispatch_async.
There are also NSOperationQueue with NSOperation and its subclasses (NSInvocationOperation & NSBlockOperation), where you can run tasks in the background as well as get notifications on main thread about finished tasks. IMHO They are more flexible than GCD in a way that you can create your own subclasses of operations as well as define dependencies between them.
The most important thing is, that you never change UI anyway in another thread except the main thread.
I think, that all points you mentioned use the same technique in the background: GDC. But I'm not sure of that.
Anyway it doesn't matter which tool you should use in terms of threading.
It's rather a question of your purpose. If you wan't to fetch an small image or just few data you can use contentsOfURLin a performSelectorInBackground() or a GDC dispatch block.
If it's about more data and more information like progress or error handling you should stick with *NSURLConnection`.
I suggest using GCD in all cases. Other techniques are still around but mainly for backward compatibility.
GCD is better for 3 reasons (at least):
It's extremely easy to use and the code remains very readable because of the use of blocks
It is lower level than things like NSOperation so it is much faster when you need high performance multi threading
It's lightweight and non-intrusive so your code doesn't have to change substantially when you want to add thread management in the middle of a method.