iOS - Concatenating asynchronous block-based operations - ios

How would you perform N asynchronous operations, such as network calls, working with completion block operations and no delegates/notifications?
Given N methods like this:
- (void)methodNWithCompletion:(void (^)(Result *))completion {
Operation *operation = [Operation new];
// ...
// Asynchronous operation performed here
// ...
return;
}
A straightforward solution would be to call each operation in the completion block of the previous one:
[self method1WithCompletion:^(Result *result) {
// ...
[self method2WithCompletion:^(Result *result) {
// ...
[self method3WithCompletion:^(Result *result) {
// ...
[self method4WithCompletion:^(Result *result) {
NSLog(#"All done");
}
}
}
}
but I'm looking for a more elegant and reusable solution, easier to write and maintain (with no many indented blocks).
Many thanks,
DAN

It all depends on what you want to do. Many powerful sophisticated tools are at your disposal. You can use such things as:
Serial queue (if you want the completion blocks run in order)
Concurrent queue (if you don't care whether the completion blocks execute simultaneously or in what order)
Dispatch group (if there is something you want to do only after all completion blocks have finished)
Operation and OperationQueue (if you want to establish the dependency order in which networking operations must take place - see esp. the fantastic WWDC 2015 video on this topic)

Related

NSOperationQueue : cancel an operation after a timeout given

Basically, I would like to perform a cancel if the operation I'm adding to the queue does not respond after a certain timeout :
NSOperationQueue * queue = ...
[self.queue addOperationWithBlock:^{
// my block...
} timeoutInSeconds:5.0 hasTimedOutWithBlock:^{
// called after 5.0, operation should be canceled at the end
}];
Thanks Guys !
You could do something like you asked for, but I might suggest adding a parameter to the first block by which the first block could check to see if the operation was canceled.
[queue addOperationWithBlock:^(NSOperation *operation) {
// do something slow and synchronous here,
// if this consists of a loop, check (and act upon) `[operation isCancelled]` periodically
} timeout:5.0 timeoutBlock:^{
// what else to do when the timeout occurred
}];
Maybe you don't need to check isCancelled, but in some cases you would (generally the burden for responding to a cancellation rests on the operation itself), so that's probably a prudent parameter to add.
Anyway, if that's what you wanted, you might do something like the following:
#implementation NSOperationQueue (Timeout)
- (NSOperation *)addOperationWithBlock:(void (^)(NSOperation *operation))block timeout:(CGFloat)timeout timeoutBlock:(void (^)(void))timeoutBlock
{
NSBlockOperation *blockOperation = [[NSBlockOperation alloc] init]; // create operation
NSBlockOperation __weak *weakOperation = blockOperation; // prevent strong reference cycle
// add call to caller's provided block, passing it a reference to this `operation`
// so the caller can check to see if the operation was canceled (i.e. if it timed out)
[blockOperation addExecutionBlock:^{
block(weakOperation);
}];
// add the operation to this queue
[self addOperation:blockOperation];
// if unfinished after `timeout`, cancel it and call `timeoutBlock`
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// if still in existence, and unfinished, then cancel it and call `timeoutBlock`
if (weakOperation && ![weakOperation isFinished]) {
[weakOperation cancel];
if (timeoutBlock) {
timeoutBlock();
}
}
});
return blockOperation;
}
#end
Having provided that code sample, I must confess that there are a very narrow set of situations where something like the above might be useful. Generally it would be better addressed using another pattern. The vast majority of the time, when you want a cancelable operation, you would implement a NSOperation subclass (often a concurrent NSOperation subclass). See the Defining a Custom Operation Object section of the Operation Queues chapter of the Concurrency Programming Guide for more information.

How to dispatch code blocks to the same thread in iOS?

Main aspect of the question: It's about iOS. Can I somehow dispatch code blocks in a way, that they will all (a) run in background and (b) on the same thread? I want to run some time-consuming operations in background, but these have to be run on the same thread, because they involve resources, that mustn't be shared among threads.
Further technical details, if required: It's about implementing an sqlite plugin for Apache Cordova, a framework for HTML5 apps on mobile platforms. This plugin should be an implementation of WebSQL in the means of the Cordova's plugin API. (That means, it's not possible to wrap entire transactions within single blocks, what could make everything easier.)
Here is some code from Cordova's Docs:
- (void)myPluginMethod:(CDVInvokedUrlCommand*)command
{
// Check command.arguments here.
[self.commandDelegate runInBackground:^{
NSString* payload = nil;
// Some blocking logic...
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:payload];
// The sendPluginResult method is thread-safe.
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
But as far as I know, there is no guarantee, that those dispatched code blocks (see runInBackground) will run on the same thread.
GCD makes no guarantee that two blocks run on the same thread, even if they belong to the same queue (with the exception of the main queue, of course). However, if you're using a serial queue (DISPATCH_QUEUE_SERIAL) this isn't a problem as you then know that there is no concurrent access to your data.
The man page for dispatch_queue_create says:
Queues are not bound to any specific thread of execution and blocks submitted to independent queues may execute concurrently.
I'm not aware of any way to bind a queue to a specific thread (after all, not needing to care about threads is a major point of GCD). The reason why you can use a serial queue without worrying about the actual thread is this promise:
All memory writes performed by a block dispatched to a serial queue are guaranteed to be visible to subsequent blocks dispatched to the same queue.
That is, a memory barrier seems to be used.
When dealing with threading issues, your main concern is usually to avoid that two threads access something concurrently. If you're using a serial queue you do not have this problem. It usually doesn't really matter which thread is accessing your resources. For example, we're using serial queues to manage Core Data access without a problem.
Edit:
It seems you really found a rare case where you need to be working on the same thread. You could implement your own worker thread:
Prerequisites:
A NSMutableArray (let's call it blockQueue).
A NSCondition (let's call it queueCondition).
Create a new NSThread.
The thread's method has an endless loop in which it locks the condition, waits for it if the queue is empty (and the "quit" bool is false), dequeues a block and executes it.
A method that locks the condition an enqueues the block.
Due to the condition, the thread will simply sleep while there's no work to do.
So, roughly (untested, assuming ARC):
- (void)startWorkerThread
{
workerThread = [[NSThread alloc]
initWithTarget:self
selector:#selector(threadMain)
object:nil
];
[workerThread start];
}
- (void)threadMain
{
void (^block)();
NSThread *currentThread;
currentThread = [NSThread currentThread];
while (1) {
[queueCondition lock];
{
while ([blockQueue count] == 0 && ![currentThread isCancelled]) {
[queueCondition wait];
}
if ([currentThread isCancelled]) {
[queueCondition unlock];
return;
}
block = [blockQueue objectAtIndex:0];
[blockQueue removeObjectAtIndex:0];
}
[queueCondition unlock];
// Execute block outside the condition, since it's also a lock!
// We want to give other threads the possibility to enqueue
// a new block while we're executing a block.
block();
}
}
- (void)enqueue:(void(^)())block
{
[queueCondition lock];
{
// Copy the block! IIRC you'll get strange things or
// even crashes if you don't.
[blockQueue addObject:[block copy]];
[queueCondition signal];
}
[queueCondition unlock];
}
- (void)stopThread
{
[queueCondition lock];
{
[workerThread cancel];
[queueCondition signal];
}
[queueCondition unlock];
}
Untested Swift 5 port:
var workerThread: Thread?
var blockQueue = [() -> Void]()
let queueCondition = NSCondition()
func startWorkerThread() {
workerThread = Thread() {
let currentThread = Thread.current
while true {
self.queueCondition.lock()
while self.blockQueue.isEmpty && !currentThread.isCancelled {
self.queueCondition.wait()
}
if currentThread.isCancelled {
self.queueCondition.unlock()
return
}
let block = self.blockQueue.remove(at: 0)
self.queueCondition.unlock()
// Execute block outside the condition, since it's also a lock!
// We want to give other threads the possibility to enqueue
// a new block while we're executing a block.
block()
}
}
workerThread?.start()
}
func enqueue(_ block: #escaping () -> Void) {
queueCondition.lock()
blockQueue.append(block)
queueCondition.signal()
queueCondition.unlock()
}
func stopThread() {
queueCondition.lock()
workerThread?.cancel()
queueCondition.signal()
queueCondition.unlock()
}
In GCD: no, that's not possible with the current lib dispatch.
Blocks can be executed by dispatch lib on whatever thread which is available, no matter to which queue they have been dispatched.
One exception is the main queue, which always executes its blocks on the main thread.
Please file a feature request to Apple, since it seems justified and sound. But I fear it's not feasible, otherwise it would already exist ;)
You can use NSOperationQueue. You can make it use just one thread by using method - (void)setMaxConcurrentOperationCount:(NSInteger)count. Set it to 1
Create a serial dispatch queue, and dispatch all the calls to that serial dispatch queue. All the calls will be performed in the background, but sequentially on the same thread.
If you want to perform a selector in Main Thread, you can use
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
and if you want to it to perform in background thread
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)object
and if you want to perform in any other thread use GCD(Grand Central Dispatch)
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//code to be executed on the main queue after delay
});
You can either use NSOperationQueue with a MaxConcurrentOperationCount of 1 or go the manual way down the road by using NSThread model (rather than Grand Central Dispatch).
Using the latter I would recommend you to implement a worker-method which runs in a thread and pulls work-packages (or commands) from a queue or a pool that is being feed from outside of the thread. Just make sure you use Locks / Mutex / Synchronisation.
Never tried this but this might do the trick. Use separate properties of atomic dispatch queues for each operation.
#property (strong, atomic) dispatch_queue_t downloadQueue;
Queue/Thread 1 for first operation
downloadQueue = dispatch_queue_create("operation1", NULL);
etc.
Since atomic is thread safe, downloadQueue should not be accessed by other threads. So it makes sure that there will be only single thread per operation and other threads will not access it.
Just like this,
dispatch_asyn(dispatch_get_current_queue, ^ {
});

Handle concurrency and asynchronous response

I am trying to implement concurrency in objective C. I have a problem with an actions that needs to be run in a synchronized way. The problem here is that I use function that executes a block after completion.
I want to connect to a bluetooth device to run some operations and connect to the next device.
for (Beacon * beacon in beacons) {
[beacon setDelegate:self];
[beacon connectToBeacon];
}
But the connection is asynchronous. The beacon call the delegate (in this case it's the same class) method didConnectSuccess when connection is successful.
I need to wait all my operations in "beaconDidConnect" and deconnection to finish before connecting to the next device.
I currently use a combination of dispatch queue and dispatch semaphore, my semaphore is an ivar
dispatch_queue_t myCustomQueue;
myCustomQueue = dispatch_queue_create("com.example.MyCustomQueue", NULL);
for (Beacon * beacon in beacons) {
[beacon setDelegate:self];
dispatch_async(myCustomQueue, ^{
dispatch_semaphore_wait(semaphoreBluetooth, DISPATCH_TIME_FOREVER);
[beacon connectToBeacon];
});
}
In combination with
- (void)beaconDidDisconnect:(Beacon *)beacon
{
dispatch_semaphore_signal(semaphoreBluetooth);
}
Without the dispatch_async, by blocking the callback (beaconDidConnect), the wait was causing a deadlock.
I wanted to dispatch_semaphore_wait in the for loop and not in the dispatch block but the wait causes the callback to wait again, causing a deadlock.
This way it seems to work but I found it a bit ugly.
My other issue is that in my beaconDidConnect method I need to chain asynchronous call and in each waiting the previous to terminate.
All those calls have a termination block, executing when the call is done. I could write instructions in deeper and deeper block but I'd like to avoid this.
I'd need an equivalent of the javascript "promise" concept.
Currently I have something with dispatch queue and dispatch semaphore but I sometimes have deadlock for unknown reason.
Eg :
- (void)beaconConnectionDidSucceeded:(Beacon *)beacon
{
dispatch_semaphore_t semaphoreEditing = dispatch_semaphore_create(1);
dispatch_queue_t editingQueue = dispatch_queue_create("com.example.MyCustomQueue.Editing", NULL);
// First writing procedure
dispatch_async(editingQueue, ^{
dispatch_semaphore_wait(semaphoreEditing, DISPATCH_TIME_FOREVER);
[beacon writeSomeCaracteristic:caracteristic withValue:value withCompletion:^(void) {
dispatch_semaphore_signal(semaphoreEditing);
}];
});
// A unknow number of writing sequences
dispatch_async(editingQueue, ^{
dispatch_semaphore_wait(semaphoreEditing, DISPATCH_TIME_FOREVER);
[beacon writeSomeCaracteristic:caracteristic withValue:value withCompletion:^(void) {
dispatch_semaphore_signal(semaphoreEditing);
}];
});
//
// ...
//
dispatch_async(editingQueue, ^{
dispatch_semaphore_wait(semaphoreEditing, DISPATCH_TIME_FOREVER);
[beacon writeSomeCaracteristic:caracteristic withValue:value withCompletion:^(void) {
dispatch_semaphore_signal(semaphoreEditing);
}];
});
// Terminate the edition
dispatch_async(editingQueue, ^{
dispatch_semaphore_wait(semaphoreEditing, DISPATCH_TIME_FOREVER);
[beacon disconnectBeacon];
dispatch_semaphore_signal(semaphoreEditing);
});
}
I want to write clear code that execute my instructions in a sequential way.
If your asynchronous methods do have a completion handler, you can "serialize" or "chain" a number of asynchronous calls like shown below:
[self asyncFooWithCompletion:^(id result){
if (result) {
[self asyncBarWithCompletion:^(id result){
if (result) {
[self asyncFoobarWithCompletion:^(id result){
if (result) {
...
}
}];
}
}];
}
}];
Of course, this gets increasingly confusing with the number of chained asynchronous calls, and especially when you want to handle errors, too.
With a third party library which especially helps to overcome these problems (including error handling, cancellation) it may look similar as the code below:
Given:
- (Promise*) asyncFoo;
- (Promise*) asyncBar;
- (Promise*) asyncFoobar;
"Chaining" the three asynchronous methods including error handling:
[self asyncFoo]
.then(^id(id result){
... // do something with result of asyncFoo
return [self asyncBar];
}, nil)
.then(^id (id result){
... // do something with result of asyncBar
return [self asyncFoobar];
}, nil)
.then(^id(id result) {
... // do something with result of asyncFoobar
return nil;
},
^id(NSError*error){
// "catch" any error from any async method above
NSLog(#"Error: %#", error);
return nil;
});
For general info about "Promises", please read wiki article Futures and Promises.
There are number of Objective-C libraries which implement a Promise.
Have you considered use NSOperation and NSOperationQueue?
If you need to wait for every beacon to run a set of operations before continue, you can store every set of operations in a NSOperation and put all the operations inside a NSOperationQueue with a maxConcurrentLimit of 1. It might be easier to cancel/pause/terminate every single operation and the queue will take care of the concurrency.
I kept the dispatch_queue and dispatch_semaphore for the connection but for the writing actions I use a library called Sequencer I found here.
It follows the Promises principle CouchDeveloper talked about.

Waiting for several networking async calls in iOS

From a view controller, as a result of a button action, I need to create a custom object that manages a set of asynchronous remote service calls, and call the method of such object that fires those service calls. I need the view controller to wait for all the async networking operations to have finished in order to update its view. Since the networking operations are async, I don't know how I'd communicate from the custom object managing this tasks to the view controller when all operations are done.
Here is the code I currently have. The code snippet in the view controller is like this (result var is not currently used):
- (void)loadData
{
BOOL __block result = NO;
dispatch_queue_t queue = dispatch_queue_create(dataLoadQueue, NULL);
dispatch_async(queue,^{
Loader *loader = [[Loader alloc] init];
[loader loadData];
dispatch_async(dispatch_get_main_queue(), ^{
if (result) {
// Update view and notify success
}
else {
// Update view and notify error
}
});
});
dispatch_release(queue);
}
And this is the loader custom object side:
- (void)loadData
{
if ([Reachability checkNetStatus]) {
Service1 *service1 = [[Service1 alloc] init];
[service1 callAsyncService];
Service2 *service2 = [[Service2 alloc] init];
[service2 callAsyncService];
// More service calls
}
else {
// Notify network not reachable
}
}
Objects service1, service2... serviceN conform the NSURLConnectionDelegate and I notify they have finished in its connectionDidFinishLoading: by means of the NSNotificationCenter (loader object is listening for such notifications). Then, I donĀ“t know what is the correct way of making loader wait for all the networking operations, and notify back the view controller.
Thanks in advance
There are probably lots of ways you could do this. First, I don't think there's any need to use GCD in the view controller -- loader is already doing things asynchronously, so the creation of loader is fast.
As for how Loader knows when all its network operations are done, you could just keep a list of strings in a mutable array, like "1 done", "2 done", etc. that would be the same as strings sent in the user info of the notifications called in connectionDidFinishLoading:. All the services could send the same notification, but with different user info. In the selector for the observer, remove the string identical to the one in the user info, and check if the array is empty -- when it is, all your services are done. At that point, I would use a delegate method to pass back the data to the view controller. Something like this in Loader:
- (void)viewDidLoad {
[super viewDidLoad];
self.doneStrings = [#[#"1 done", #"2 done", #"3 done", #"4 done"] mutableCopy];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notificationReceived:) name:#"SeriveFinishedNotification" object:nil];
}
-(void)notificationReceived:(NSNotification *) aNote {
[self.doneStrings removeObjectIdenticalTo:[aNote.userInfo objectForKey:#"doneString"]];
if (self.doneStrings.count == 0)
[delegate doSomethingWithTheData: theData];
}
You would probably need to some other things like handle the case where some of the network operations fail.
If you want to wait until the async tasks were done, you can use a semaphore. See the example below, the logic is pretty simply. I think you can easily adapt to your case.
//create the semaphore
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[objectManager.HTTPClient deletePath:[address addressURL] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//some code here executed in background
dispatch_semaphore_signal(semaphore); //sends a notification to release the semaphore
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//some other code here also executed in background
dispatch_semaphore_signal(semaphore); //sends a notification to release the semaphore
}];
//holds the thread until the dispatch_semaphore_signal(semaphore) is send
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
You haven't shared the details of how these asynchronous requests work, but another approach is to make these asynchronous requests NSOperation objects that you submit to a NSOperationQueue. (AFNetworking is an example of this sort of implementation.) When you do that, you can create yet another NSOperation to be triggered upon the completion of the network request operations, by make it dependent upon those network request operations. Thus it will only run when all of the network requests are done. Using an NSOperation-based solution enjoys other benefits, too (e.g. you can use setMaxConcurrentOperationCount to let you enjoy concurrency, but not run too many concurrent requests at any given time).
References
Ray Wenderlich's How To Use NSOperations and NSOperationQueues
Defining a Custom Operation Object in Apple's Concurrency Programming Guide

Parsing JSON data from WebService on a background thread

I have a method that builds a package, sends it to a web service, gets a package back, opens it and returns me a nsdictionary. How can I call it on a background queue in order to display a HUD while it requests the data?
You could detach a new thread like following
- (void) fetchData
{
//Show Hud
//Start thread
[NSThread detachNewThreadSelector:#selector(getDataThreaded)
toTarget:self
withObject:nil];
}
- (void) getDataThreaded
{
//Start Fetching data
//Hide hud from main UI thread
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI if you have to
//Hide Hud
});
}
Grand central dispatch (gcd) provides great support for doing what you ask. Running something in the background using gcd is simple:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_NORMAL, 0) ^{
NSDictionary* data = [self fetchAndParseData];
dispatch_async(dispatch_get_main_queue(), ^{
[self dataRetrieved:data];
});
});
This call will return immediately (so your UI will continue to be responsive) and dataRetrieved will be called when the data is ready.
Now, depending on how fetchAndParse data works it may need to be more complicated. If you NSURLConnection or something similar, you might need to create an NSRunLoop to process data callbacks on the gcd thread. NSURLConnection for the most part is asynchronous anyway (though callbacks like didReceiveData will be routed through the UI thread) so you can use gcd only to do the parsing of the data when all the data has been retrieved. It depends on how asynchronous you want to be.
In addition to previous replies, why don't you use NSOperation and NSOperationQueue classes? These classes are abstractions under GCD and they are very simple to use.
I like NSOperation class since it allows to modularize code in apps I usually develop.
To set up a NSOperation you could just subclass it like
//.h
#interface MyOperation : NSOperation
#end
//.m
#implementation MyOperation()
// override the main method to perform the operation in a different thread...
- (void)main
{
// long running operation here...
}
Now in the main thread you can provide that operation to a queue like the following:
MyOperation *op = [[MyOperation alloc] initWithDocument:[self document]];
[[self someQueue] addOperation:op];
P.S. You cannot start an async operation in the main method of a NSOperation. When the main finishes, delegates linked with that operations will not be called. To say the the truth you can but this involves to deal with run loop or concurrent behaviour.
Here some links on how to use them.
http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/
https://developer.apple.com/cocoa/managingconcurrency.html
and obviously the class reference for NSOperation

Resources