Do RestKit completion blocks run on the main UI thread? - ios

My code tells me it is via [NSThread currentThread] isMainThread] and the fact that I can show a UIAlertView as well. This seems unexpected because my understanding is that requests are dispatched asynchronously.
I'd throw in some relevant code, but it's nothing special. I POST an object via the RKObjectManager and the success and failure completion blocks execute on the main UI thread (even though I am definitely doing stuff in the background after the request completes).
I'm confused.
What's going on?

RKObjectManager behind the scenes creates and posts an RKObjectRequestOperation (which is essentially representation of your POST request) into NSOperationQueue to perform all the networking and mapping stuff asynchronously in the background thread.
When request is finished and response processed - default behavior is to run success or failure callbacks on the main thread. This is nice quick setup that helps you to build an app very fast and easy (and not worry about updating UI on the right thread).
You can easily change that behavior, RKObjectRequestOperation has 2 properties, successCallbackQueue and failureCallbackQueue, you can set them to be another background thread, and then when you need to change something on the UI side, just wrap that change into a block and post to main queue:
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
}];

The reason why you want requests are dispatched asynchronously because you don't want UI Thread (which is main thread) blocked by the request. because when it blocked , your App wont react user's action , that's a bad user experience.
Then after the request is done, you want to let the user know it, so the dispatched thread must return to main thread. in your situation, that's the completion block. then you can do things like show alert on the main thread.
I hope this is the answer you want.

Related

Returning to a thread with GCD from a delegate

We're implementing a sync system where some operations are sent to a SAP Mobile Platform server asynchronously, and when the operations are completed, we receive a delegate call from a proprietary SDK.
This delegate method is run on a random thread, not using GCD.
We would like to do send the operations - then wait until the delegate method is called which means the operations are complete - then resume the work when this is finished (or a time-out has occured). I know this may seem synchronous, but we cannot allow the user to modify data while the operations are not finished yet to ensure data integrity.
The Sync ViewController is doing some heavy syncing work and is using GCD, and updates a progress bar and some text in the UI thread.
Now, when the delegate method is called, we want to call another method on the Sync ViewController, but this call is apparently done in the wrong thread.
My question is how to execute these methods in the same thread that GCD was executing them before the delegate was called.
Things we tried:
Just calling the methods. Then these methods are called in the wrong thread, namely the thread where the delegate method is in.
Posting a notification on a chosen thread, we don't know the exact thread where the Sync ViewController was working in.
Any ideas?
If you are already using GCD, then you must know the dispatch queue this work is being done on, so isn't it simply a case of scheduling a block, either synchronously or asynchronously, on that queue?
- (void)someDelegateMethod:(id)someValue {
dispatch_async(self.myDispatchQueue, ^{
[self doInterestingThingWith:someValue];
});
}

AFNetworking - Serialize Connection

I use AFNetworking as my connection lib to my app. Due to the back-end restrictions, I cannot send two request simultaneously when the app starts because the server will identify a CookieTheftException (Grails). After a first successful connection, I can do as many simultaneous requests as I want but the first need to be serial.
How can I achieve that?
I thought using a Semaphore but i can't block the main thread.
Edit 1
I tried to override but it didn't work. I think the operation queue doesn't wait one request to finish (including it's callback) to start the other.
- (void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation
{
[self.operationQueue setMaxConcurrentOperationCount:1];
[super enqueueHTTPRequestOperation:operation];
}
Edit 2
I realized that the maxConcurrentOperation worked and in fact 1 operation is executed at a time. The problem is that the request enqueued is already created without the cookies the server needs.
I don't know anything about Grails or the specific architecture of your system, but perhaps this could be solved by simply turning off cookies on that request, with NSMutableURLRequest -setHTTPShouldHandleCookies:.
Other than that, the best way to ensure that only one request operation is ever running for that initial call would be to ignore queues altogether, and simply have an AFHTTPRequestOperation property on your AFHTTPClient subclass. You could even get fancy with KVO to ensure that the operation queue is suspended until that initial request is finished.
I would recommend to read about GCD
You can create a queue and put some block to be executed in this queue.
This way:
It won't block main thread
Since all your networking blocks will be executed in one queue, there is no way that two blocks will be executed simultaneously.
You could set the maximum concurrent operations of the queue to 1. That way only one request will be made at a time.
[self.httpClient.operationQueue setMaxConcurrentOperationCount:1];
But since you only need to wait for the first request, why not just call that request on its own, then initialize the other requests only when the first one completed?

concurrent NSOperation, why start on main thread?

I am studying this source code which demonstrates how to use NSURLConnection with NSOperation:
link
I am confused about the code at line 76
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:#selector(start)
withObject:nil waitUntilDone:NO];
return;
}
Why is the author making sure that the code is run on main thread?
Isn't the whole point of NSOperation to not run on main thread and in a background thread so that it doesn't block?
The code is from this article that explains it although it doesn't answer my questions: http://eng.pulse.me/concurrent-downloads-using-nsoperationqueues/
From the NSURLConnection docs, you can see;
NSURLConnection’s delegate methods—defined by the NSURLConnectionDelegate Protocol protocol—allow an object to receive informational callbacks about the asynchronous load of a URL request. [...] These delegate methods are called on the thread that started the asynchronous load operation for the associated NSURLConnection object.
Starting an operation on an NSURLConnection works on any thread, however it's very useful to get the delegate callbacks on the GUI/main thread if you want to - for example - display progress.
If you use NSURLConnection asynchronous you need to launch this operation on Main Thread for getting the callback. If you launch an asynchronous NSURLConnection from a background thread you can lose it callback if your background thread from you launched your NSURLConnection is busy for other activity.
If you call start method in main thread, A current operation main method will be run in main thread, but you push current operation to NSOperationQueue(not main queue), start method call in sub thread, a current operation main method in sub thread.
Sub Thread Photo
Main Method in Sub Thread
If this is an example I believe that this is put into there in order for you to see the difference between having that code in there and not. If this code were to be executing concurrently and is supposed to leave your main thread alive then indeed that section of code should not be there however it may have been put in there for you to remove and see the difference. However you are indeed correct. Browsing through the rest of the file it looks like that should not be in there if you are wanting to leave your main thread open.
The reason is, on iOS 4.0+, wether the operation is concurrent or not, the operation is ran in a background thread. Since in this case the operation is concurrent, the method exists immediately and the thread is killed so no delegate method is called (NSURLConnection calls delegate method on the thread from which it started).
The only options are, either to start an NSRunLoop (very hectic) or use a thread that already has one (main thread) - so that's why the start method is run from the main thread.
Has nothing to do with updating UI as many suggested (although i understand the point but the entire reason to run an NSURLConnection in a queue is to process the delegate callbacks in a seperate thread to avoid blocking UI). That wasn't the intention of the author, the fact that it is UIKit safe is a mere consequence.

NSURLConnection and sendAsynchronousRequest:queue:completionHandler: - does the completion block run in the main thread

If I use NSURLConnection with sendAsynchronousRequest:queue:completionHandler:, do I need to take any particular precautions inside the completion handler? Specifically, is sending asynchronously directly via NSURLConnection the same/similar as using dispatch_async in that the completion block would need to come back to the main thread to interact with the UI?
For example: after using dispatch_async to interact (maybe the wrong word) with the UI, the app must use performSelectorOnMainThread.
Is that a consideration at all with sendAsynchronousRequest:queue:completionHandler:?
note: I have found similar questions, but not this specifically. eg, in Does NSURLConnection block the main thread? indiekiduk specifically states it is a new thread, albeit an internal one--I think that is flawed, since I thought it was just at the end of the run loop--but I am still learning and probably/possibly wrong.
The documentation for NSURLConnection states for the queue parameter to sendAsynchronousRequest:queue:completionHandler:
queue
The operation queue to which the handler block is dispatched when the request completes or > failed.
The block will execute on whichever thread the passed in queue was set up on.
NSURLConnection sendAsync is scheduled on the RunLoop of the thread its on AFAIK
so the block is called in the thread you start it

What's the difference between performing an NSURLRequest and responding to delegates as opposed to using dispatch_async?

I'm trying to get my head around a asynchronicity; dispatch, multiple threads, run loops etc etc.
What's the difference between:
1) creating an NSURLRequest and NSURLConnection in a given method, and having it execute and us responding to the delegate methods (didReceiveResponse, didReceiveData, connectionDidFinishLoading, etc), and
2) creating a block and having it dispatch_async ?
With the first method it seems great that I have access to the delegate methods (do I still have access to those using dispatch?), and execution of the delegate methods are actioned when fired (or close to it?)
With the block/dispatch method, I'm guessing the block is processed synchronously within it's thread? And then comes back to the main thread to process the results? Example code I've been looking at:
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:
kLatestKivaLoansURL];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
});
So, the "self performSelector...." is performed AFTER the data is received? (what I meant before with synchronously - maybe the wrong term). And the next line of the block sends us back to the main thread.
What is the purpose of, or why is there a "waitUntilDone:YES"? Is it because if it's not there, other stuff could happen NOT in the main thread?
Is the first method above still only performed on the main thread?
and finally, what are the pros and cons of each in the case of a JSON query to a web page? Is there any great benefit of one method over the other?
1) When you use a NSURLConnection, either in your main thread or say in a NSOperation, you have full control of stopping it at any point, and tracking its progress. What you get back are delegate methods when various things happen, but you're not sitting and waiting for something to complete. If you want to stop it at anytime, you send it cancel, then you can release (or nil) it and forget about it.
2) So look at this. If you did this call in the main thread, it would wait until it succeeds or fails. Once started, it has to run to success or failure. This would block your UI if in the main thread. Put it in a block and run it on some other thread, and the same thing will happen - the chosen thread will block until the method ends. The use of 'self' to send the result will retain self. So if self is say a UIViewController, then even if you pop it (thinking its going to get removed), it stays around until this method completes, doing heaven knows what. This usage is very dangerous and may work often but fail catastrophically when a device has a crappy inet connection (for example).
The wait until does is to synchronize threads. If you want to know that the method has been run on the main thread, and only then proceed, use YES. If you just want the method queued and you're done (like in this case) you would just use NO.
The method will for sure be on the main thread - this Apple promises you with this method.
3) JSON
The majority of users I know use NSURLConnections from NSOperations or blocks. Operations can be cancelled (which would then cancel the connections) - so you can handle getting a 'Back' button press no matter how much is going on. If the query fails you can look at the html status (did you get a 400 or 500 error? a timeout? etc)
There is an open source project on github, hardly 200 lines of code, that provide an elegant and easy to use helper class to run operations (with demo code): /NSOperation-WebFetches-MadeEasy. I have personally used this code in over 8 apps still in the store with great success - a single OperationsRunner often has hundreds of fetches going on simultaneously, and the app has multiple classes running OperationsRunners at the same time.
If you process the JSON in the NSOperation, you will get real speedups on devices with multiple cores.

Resources