How To Set AFHTTPRequestOperation completionQueue to current queue - ios

I have a layer of abstraction between AFNetworking and the rest of my iOS app. And I have an issue where sometimes - (void)setCompletionBlockWithSuccess:failure: gets called from inside a block on the main thread: dispatch_async(dispatch_get_main_queue(), block) and other times inside this block on another thread: dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block)
But unless I set the completionQueue to the current queue, the completion block get's thrown back to the main thread. However, I need the code to run on the thread from which I was previously running on.
Is there a good way to achieve this? Apparently dispatch_get_current_queue() is deprecated and was never appropriate for production code anyway.
EDIT: realize this probably won't work with a concurrent queue since it doesn't guarantee same thread. So instead I'll create a custom dispatch_queue_t myCustomQueue;
myCustomQueue = dispatch_queue_create("com.example.MyCustomQueue", NULL); Which I believe will put me back on the same thread. EDIT2: I guess I was wrong, It still puts it on another thread. Lame. I need it to be the same one.

As you've discovered empirically, other than the main queue and main thread, there is no reliable relationship between GCD queues and OS threads. If you want thread affinity here, you could set completionQueue to a global concurrent queue, and then have the completion block use - performSelector:onThread:withObject:waitUntilDone: to marshal the real work to a different thread. Imagine something like this:
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation* operation, id responseObject) {
dispatch_block_t realSuccessCompletion = ^{
NSAssert([[NSThread currentThread] isEqual: intendedThread], #"Wrong thread");
// Do real work.
};
// Send it to the thread you want it run on.
[(id)realSuccessCompletion performSelector: #selector(invoke) onThread: intendedThread withObject: nil waitUntilDone: NO];
} failure:^(AFHTTPRequestOperation* operation, NSError *error) {
dispatch_block_t realFailureCompletion = ^{
NSAssert([[NSThread currentThread] isEqual: intendedThread], #"Wrong thread");
// Do real work.
};
// Send it to the thread you want it run on.
[(id)realFailureCompletion performSelector: #selector(invoke) onThread: intendedThread withObject: nil waitUntilDone: NO];
}];
This takes advantage of the fact that blocks behave like Objective-C objects which respond to the selector -invoke. Note that this also relies on the intended thread being long-lived and having a run loop, although if the thread were ephemeral, then it's not clear why you would want to run things on the same thread later.

Related

NSOperation and NSOperationQueue working thread vs main thread

I have to carry out a series of download and database write operations in my app. I am using the NSOperation and NSOperationQueue for the same.
This is application scenario:
Fetch all postcodes from a place.
For each postcode fetch all houses.
For each house fetch inhabitant details
As said, I have defined an NSOperation for each task. In first case (Task1), I am sending a request to server to fetch all postcodes. The delegate within the NSOperation will receive the data. This data is then written to database. The database operation is defined in a different class. From NSOperation class I am making a call to the write function defined in database class.
My question is whether the database write operation occur in main thread or in a background thread? As I was calling it within a NSOperation I was expecting it to run in a different thread (Not MainThread) as the NSOperation. Can someone please explain this scenario while dealing with NSOperation and NSOperationQueue.
My question is whether the database write operation occur in main
thread or in a background thread?
If you create an NSOperationQueue from scratch as in:
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
It will be in a background thread:
Operation queues usually provide the threads used to run their
operations. In OS X v10.6 and later, operation queues use the
libdispatch library (also known as Grand Central Dispatch) to initiate
the execution of their operations. As a result, operations are always
executed on a separate thread, regardless of whether they are
designated as concurrent or non-concurrent operations
Unless you are using the mainQueue:
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
You can also see code like this:
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
[myQueue addOperationWithBlock:^{
// Background work
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// Main thread work (UI usually)
}];
}];
And the GCD version:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
{
// Background work
dispatch_async(dispatch_get_main_queue(), ^(void)
{
// Main thread work (UI usually)
});
});
NSOperationQueue gives finer control with what you want to do. You can create dependencies between the two operations (download and save to database). To pass the data between one block and the other, you can assume for example, that a NSData will be coming from the server so:
__block NSData *dataFromServer = nil;
NSBlockOperation *downloadOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakDownloadOperation = downloadOperation;
[weakDownloadOperation addExecutionBlock:^{
// Download your stuff
// Finally put it on the right place:
dataFromServer = ....
}];
NSBlockOperation *saveToDataBaseOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakSaveToDataBaseOperation = saveToDataBaseOperation;
[weakSaveToDataBaseOperation addExecutionBlock:^{
// Work with your NSData instance
// Save your stuff
}];
[saveToDataBaseOperation addDependency:downloadOperation];
[myQueue addOperation:saveToDataBaseOperation];
[myQueue addOperation:downloadOperation];
Edit: Why I am using __weak reference for the Operations, can be found here. But in a nutshell is to avoid retain cycles.
If you want to perform the database writing operation in the background thread you need to create a NSManagedObjectContext for that thread.
You can create the background NSManagedObjectContext in the start method of your relevant NSOperation subclass.
Check the Apple docs for Concurrency with Core Data.
You can also create an NSManagedObjectContext that executes requests in its own background thread by creating it with NSPrivateQueueConcurrencyType and performing the requests inside its performBlock: method.
From NSOperationQueue
In iOS 4 and later, operation queues use Grand Central Dispatch to execute operations. Prior to iOS 4, they create separate threads for non-concurrent operations and launch concurrent operations from the current thread.
So,
[NSOperationQueue mainQueue] // added operations execute on main thread
[NSOperationQueue new] // post-iOS4, guaranteed to be not the main thread
In your case, you might want to create your own "database thread" by subclassing NSThread and send messages to it with performSelector:onThread:.
The execution thread of NSOperation depends on the NSOperationQueue where you added the operation. Look out this statement in your code -
[[NSOperationQueue mainQueue] addOperation:yourOperation]; // or any other similar add method of NSOperationQueue class
All this assumes you have not done any further threading in main method of NSOperation which is the actual monster where the work instructions you have (expected to be) written.
However, in case of concurrent operations, the scenario is different. The queue may spawn a thread for each concurrent operation. Although it's not guarrantteed and it depends on system resources vs operation resource demands at that point in the system. You can control concurrency of operation queue by it's maxConcurrentOperationCount property.
EDIT -
I found your question interesting and did some analysis/logging myself. I have NSOperationQueue created on main thread like this -
self.queueSendMessageOperation = [[[NSOperationQueue alloc] init] autorelease];
NSLog(#"Operation queue creation. current thread = %# \n main thread = %#", [NSThread currentThread], [NSThread mainThread]);
self.queueSendMessageOperation.maxConcurrentOperationCount = 1; // restrict concurrency
And then, I went on to create an NSOperation and added it using addOperation. In the main method of this operation when i checked for current thread,
NSLog(#"Operation obj = %#\n current thread = %# \n main thread = %#", self, [NSThread currentThread], [NSThread mainThread]);
it was not as main thread. And, found that current thread object is not main thread object.
So, custom creation of queue on main thread (with no concurrency among its operation) doesn't necessarily mean the operations will execute serially on main thread itself.
The summary from the docs is operations are always executed on a separate thread (post iOS 4 implies GCD underlying operation queues).
It's trivial to check that it is indeed running on a non-main thread:
NSLog(#"main thread? %#", [NSThread isMainThread] ? #"YES" : #"NO");
When running in a thread it's trivial to use GCD/libdispatch to run something on the main thread, whether core data, user interface or other code required to run on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
// this is now running on the main thread
});
If you're doing any non-trivial threading, you should use FMDatabaseQueue.

NSOperation is not happening in background thread

I created an NSOperation subclass to handle some zip archive operations. No matter what, if I override -start or -main this block of code always happens:
if ([NSThread isMainThread]) {
NSLog(#"I am in the main thread");
return;
}
Any idea what's going on?
I've tried adding this block:
- (void) start { //also tried overriding main
if ([NSThread isMainThread]) {
NSLog(#"In main thread, trying again");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self start];
});
return;
//hard working code etc...
//cpu intensive zip operations...
}
But this causes a crash, an EXC_BAD_ACCESS violation pointing at the dispatch_async line.
No matter what, if I override -start or -main this block of code always happens:
The main operation queue runs on the main thread. From the docs for +[NSOperationQueue mainQueue]:
The returned queue executes operations on the main thread. The main
thread’s run loop controls the execution times of these operations.
So, running in another thread is a matter of what queue you add the operation to, not how you write the operation's code. If you want your operation to run on a different operation queue, you'll need to create a queue of your own using
NSOperationQueue* aQueue = [[NSOperationQueue alloc] init];
You can find an example in Adding Operations to an Operation Queue in the Concurrency Programming Guide.
But this causes a crash, an EXC_BAD_ACCESS violation pointing at the dispatch_async line.
It sounds like -[NSOperation start] probably isn't re-entrant. Your code effectively executes the same method on two different threads. In fact, look at the docs for -start, it's obvious that your code won't work:
You can call this method explicitly if you want to execute your
operations manually. However, it is a programmer error to call this
method on an operation object that is already in an operation queue
or to queue the operation after calling this method. Once you add an
operation object to a queue, the queue assumes all responsibility for
it. [Emphasis added. -Caleb]
In other words, don't do that.

iOS5: Determine if core data method is running in an asynch dispatch queue?

How can I tell if a method is running asynchronously or not?
I have several core data related methods which are sometimes called asynchronously via an asynch dispatch queue (GCD) and other times are called synchronously.
// sometimes this happens
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[[DataServices sharedInstance] doSomeCoreDataStuff:^(BOOL result, NSString *message)
{
// do some post-stuff here
}];
});
// other times this happens
[[DataServices sharedInstance] doSomeCoreDataStuff:^(BOOL result, NSString *message)
{
// do some post-stuff here
}];
In addition to this, I am using a singleton NSManagedObjectContext throughout my app. Since context is not thread-safe, in the case when the core data methods are running asynchronously I need to create a new context within those methods, otherwise I want to use the singleton instance context.
The only approach that comes to mind is something like [[NSThread mainThread] isMainThread], but GCD may or may not do work on the main thread, so this will not work.
The right way to deal with this is to configure your managed object context to use one the thread confinement concurrency types-- NSPrivateQueueConcurrencyType or NSMainQueueConcurrencyType-- and then use its performBlock: method when you want to use it. That is,
[[DataServices sharedInstance] performBlock:^{
// do Core Data stuff here
}];
You can do that on any thread or queue, and it's asynchronous. There's also performBlockAndWait: if you need to get the results immediately.
You'd use that call everywhere. The only exception is if you use NSMainQueueConcurrencyType and you know that you're running on the main queue, you could make calls on the context directly instead of via performBlock:

`[NSThread isMainThread]` always returns YES

This code
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(#"Main Thread? %d", [NSThread isMainThread]);
});
shows that I'm in the main thread. Even doing this:
queue = dispatch_queue_create("nonMainQueue", NULL);
still reports that I'm in the main queue. This is, it seems, because I'm using dispatch sync.
Does this mean that my code is the same as not using dispatch_sync at all? Also: what's the point of dispatch_sync if it does nothing at all, then?
Because queues are not threads, in order to check if you are on the main 'queue', you must use different code, something similar to this:
if (dispatch_get_current_queue() == dispatch_get_main_queue()) {
NSLog(#"On Main Thread!");
}
Just note that dispatch_get_current_queue is deprecated, and is subject to be completely removed in a later iOS/Mac OS version.
This is documented behavior. As an optimization the blocks passed to dispatch_sync are executed on the current thread if possible (which is almost always).
My understanding from Apple's GCD guide, there is no guarantee that dispatch queues will execute on a separate thread. GCD will determine which thread, and if necessary create a new one.
Part of the point is now you do not have to think about threads.
The only thing to keep in mind, is to make sure you are updating UI elements on the main queue, for example:
// on background queue
dispatch_async( dispatch_get_main_queue(), ^(void){
someLabel.text = #"My Text";
});

How should I use GCD dispatch_barrier_async in iOS (seems to execute before and not after other blocks)

I'm trying to synchronize the following code in iOS5:
an object has a method which makes an HTTP request from which it
gets some data, including an URL to an image
once the data arrives, the textual data is used to populate a
CoreData model
at the same time, a second thread is dispatched async to download
the image; this thread will signal via KVO to a viewController when
the image is already cached and available in the CoreData model.
since the image download will take a while, we immediately return
the CoreData object which has all attributes but for the image to
the caller.
Also, when the second thread is done downloading, the CoreData model
can be saved.
This is the (simplified) code:
- (void)insideSomeMethod
{
[SomeHTTPRequest withCompletionHandler:
^(id retrievedData)
{
if(!retrievedData)
{
handler(nil);
}
// Populate CoreData model with retrieved Data...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:#"imageURL"]];
aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL];
});
handler(aCoreDataNSManagedObject);
[self shouldCommitChangesToModel];
}];
}
- (void)shouldCommitChangesToModel
{
dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSError *error = nil;
if(![managedObjectContext save:&error])
{
// Handle error
}
});
}
But what's going on is that the barrier-based save-block is always executed before the the image-loading block. That is,
dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSError *error = nil;
if(![managedObjectContext save:&error])
{
// Handle error
}
});
Executes before:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:#"imageURL"]];
aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL];
});
So obviously I'm not really dispatching the image-loading block before the barrier, or the barrier would wait until the image-loading block is done before executing (which was my intention).
What am I doing wrong? how do I make sure the image-loading block is enqueued before the barrier block?
At first glance the issue may be that you are dispatching the barrier block on a global concurrent queue. You can only use barrier blocks on your own custom concurrent queue. Per the GCD docs on dispatch_barrier_async, if you dispatch a block to a global queue, it will behave like a normal dispatch_async call.
Mike Ash has a good blog post on GCD barrier blocks: http://www.mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html
Good luck
T
You need to create your own queue and not dispatch to the global queues as per the ADC Docs
The queue you specify should be a concurrent queue that you create
yourself using the dispatch_queue_create function. If the queue you
pass to this function is a serial queue or one of the global
concurrent queues, this function behaves like the dispatch_async
function.
from https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_barrier_async .
You can create tons of your own GCD queues just fine. gcd queues are very small and you can create tons of them without issue. You just need to free them when you're done with them.
For what you seem to be trying to solve, dispatch_barrier_async may not be the best solution.
Have a look at the Migrating Away From Threads section of the Concurrency Programming Guide. Just using dispatch_sync on a your own serial queue may solve your synchronization problem.
Alternatively, you can use NSOperation and NSOperationQueue. Unlike GCD, NSOperation allows you to easily manage dependancies (you can do it using GCD, but it can get ugly fast).
I'm a little late to the party, but maybe next time you could try using dispatch_groups to your advantage. http://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2

Resources