Wait for many asynchronous calls to perform callback - ios

I want to synchronize some data with a web service. For each item I have to make a asynchronous call.
I want to have a completion block witch is called, when each item was synchronized. For each item I am able to perform a completion block. Now, I don't know a good way how to do it.
This is the interface:
-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
NSArray* items = // get items
for (int i = 0, n = [items count]; i < n; i++) {
[self synchronizeItem:[items objectAtIndex:i] onComplete:^{
// What do do here?
}];
}
// And/or here?
}
-(void) synchronizeItemOnComplete:(CompleteBlock) block {
// do something
block();
}
How can I wait for the synchronization and then perform the block?
I tried something like this:
NSArray* items = // get items
__block int countOfItemsUntilDone = [items count];
for (int i = 0, n = countOfItemsUntilDone; i < n; i++) {
[self synchronizeItem:[items objectAtIndex:i] onComplete:^{
countOfItemsUntilDone--;
}];
}
dispatch_queue_t queue = dispatch_queue_create("wait for syncing", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
while (countOfItemsUntilDone > 0) {
usleep(1000); // wait a little bit
}
block();
});
dispatch_release(queue);
But I think this is a quite bad way. Any ideas?

Instead of spinning in a loop waiting for the counter to equal zero, check the counter value each time you decrement it, then fire an event when it reaches zero.
-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
NSArray* items = // get items
__block NSUInteger remaining = [items count];
for (ItemClass* item in items) {
[self synchronizeItemImage:item onComplete:^{
--remaining;
if (remaining == 0) {
block();
}
}];
}
}
To explain why it feels wrong, there are two things you're doing here that you should do either never or rarely:
Using background queues. This is difficult and bug-prone. Don't do it without reading up a lot about writing concurrent code. You also only really need to do this if an operation blocks for a substantial amount of time (eg., to read a file from disk, or perform an intensive calculation). Don't assume you need to do it unless you have a good reason (eg., a measurable performance problem).
Spinning in a loop, checking a variable for changes and calling sleep. You should never do this.
Also, if you're looping over the elements in an array, the for ... in syntax is much nicer (and potentially more efficient) calling objectAtIndex: on each index.

Never check or decrement shared memory in different threads like this, it can cause races. Use a dispatch group to do what you're doing.
dispatch_queue_t myBGQueue;
dispatch_group_t itemsGroup = dispatch_group_create();
for (ItemClass *item in items) {
dispatch_group_async(itemsGroup, myBGQueue, ^{
[self synchronizeItemImage:item];
});
}
/* execution will sleep here until all the blocks added in the `for` complete */
dispatch_group_wait(itemsGroup, DISPATCH_TIME_FOREVER);
dispatch_release(itemsGroup);

You can use these to use synchronously.
GCD and this
performSelector:waitUntilDone:YES

Related

iOS: dispatch_block_t execute some part of code two times and I dont know why

When I call the following function:
+ (void) myMethod:(NSString *) myConstString array: (NSArray *) myArray
{
dispatch_block_t block =
^{
for (int i = 0; i < myArray.count; i++)
{
if ([#"myString1" isEqual: myConstString])
// Do some easy job here
else if ([#"myString2" isEqual: myConstString])
// Do some other easy job here
[NSThread sleepForTimeInterval: 0.5];
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"test" object:nil userInfo:nil];
};
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.example.test", NULL);
dispatch_async(backgroundQueue, block);
}
[[NSNotificationCenter...] is executed two times. I know that because method in other class which is responsible for "catch" this notification is called two times. First call is instant (and this is strange to me). Second call is after about 2 seconds (this call I like :-) ) I know how to "repair" this:
+ (void) myMethod:(NSString *) myConstString array: (NSArray *) myArray {
dispatch_block_t block =
^{
Boolean isForLoopExecuted = false;
for (int i = 0; i < myArray.count; i++)
{
if ([#"myString1" isEqual: myConstString])
// Do some easy job here
else if ([#"myString2" isEqual: myConstString])
// Do some other easy job here
[NSThread sleepForTimeInterval: 0.5];
isForLoopExecuted = true;
}
if (isForLoopExecuted)
[[NSNotificationCenter defaultCenter] postNotificationName:#"test" object:nil userInfo:nil];
};
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.example.test", NULL);
dispatch_async(backgroundQueue, block); }
After addind isForLoopExecuted everything works propertly. There is only one call after 2 seconds. This suggests that for loop is not executed when first call is made. I am really curious why this is happening. Thanks for your time!
First off, creating new background queue every time that function runs is probably not what you want. There are limits how many queues you can have, and this is right way to reach that limit quickly and crash.
Use something like this:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), block);
Secondly, there is nothing wrong with first listing (except that background queue) and it works correctly. Notification will be sent just once.
What I would suggest is to put log when you are sending notification to see exactly how many times you are executing that function. Probably more than once. Also, time that will be spent in queue depends on parameters you are sending there. If myArray is empty, you get notification almost immediately.

Cancel actions in dispatch_async when they're no longer needed

I have a UIViewController that does the following in viewDidLoad
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
items = [[DataFetcher sharedInstance] getItems handler:^(NSArray *currentItems){
if (currentItems.count % 30 == 0) { //don't wait for all the items to be ready show by chunks of 30
items = currentItems;
[tableView reloadData];
}
items = currentItems;
}];//Pretty complex call that takes some time to finish there is WebService calls, data parsing, storing some results ...
dispatch_async(dispatch_get_main_queue(), ^{
[tableView reloadData];
});
});
What I need to do is to stop getItems when I pop this viewController. It's pointless and it takes CPU Time and energy (This call may take up to a minute on some cases).
I am assuming I should be doing this in viewWillDisappear but how exactly?
You can use NSBlockOperation. Periodically check if it's been cancelled, and stop doing work if it has been:
- (void)getItemsWithHandler:(void (^)(NSArray *currentItems))handler {
self.operation = [NSBlockOperation blockOperationWithBlock:^{
if (self.operation.isCancelled) {
return;
}
// Do something expensive
if (self.operation.isCancelled) {
return;
}
// Do something else expensive
for (int i = 0; i < 10000; i++) {
if (self.operation.isCancelled) {
return;
}
// Do expensive things in a loop
}
}];
}
- (void) cancelGetItemsRequest {
[self.operation cancel];
self.operation = nil;
}
Alternatively, you can put a bunch of NSBlockOperations in an NSOperationQueue. You can set dependencies for the work, and cancel the entire queue at once if you want.
Cancelling asynchronous operations is nicely supported in NSOperation and NSOperationQueue, but it's quite a bit more complicated. In any case, your asynchronous code has to check from time to time whether it is cancelled or should still continue.
Best thing is to have a property "cancelled" somewhere, that you set when you don't want any more work to be done, and whenever you try to do more work, you check that property.

Independent action in loop iOS

There is some action that is executed in a loop, a certain number of iterations. In every iteration is formed string to be displayed in the UITextView.
- (IBAction)ButtonClick:(id)sender {
sTest *sT = new sTest();
int res;
for (int i=0; i<300; i++)
{
res = sT->test_fun(N1[i], M1[i], 10000);
[_textView setText:[NSString stringWithFormat:#"%#res=%d\n", _textView.text, res]];
}
With this approach, UI waits until cycle ends, and displays all the lines at once in textview. And I would like that each row displays in each iteration.
Using [NSThread detachNewThreadSelector:#selector(StringLoop) toTarget:self withObject:nil]; has no effect.
Tell me how to do it. Example is welcomed. Thank you.
I'm not sure that what you want is what you should be doing, but the most common implementation for concepts like you describe would be to run your loop on a background thread, and then make calls to the main thread to update the UI when needed. I've included one of the most "readable" and understandable implementations below, but as mentioned in comments, the Grand Central Dispatch is well-suited to your needs.
-(void)startItAll
{
[self performSelectorInBackground:#selector(doStuffInBackground) withObject:nil];
}
-(void)doStuffInBackground
{
//do math
for (int i = 0; i < 100; i++) {
[self performSelectorOnMainThread:#selector(doStuffInMain) withObject:nil waitUntilDone:NO];
}
}
-(void)doStuffInMain
{
//update necessary UI
}

How to illustrate a background task using GCD?

I want to illustrate the progress on MBProgressHUD item, but when i triger this method :
- (IBAction)signInBttn:(id)sender {
MBProgressHUD *hudd = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hudd.mode = MBProgressHUDModeAnnularDeterminate;
hudd.labelText = #"Loading";
__block float value = 0;
for (int j = 0; j<2000; j++) {
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i<20000 ; i++) {
}
value += 0.001;
dispatch_async( dispatch_get_main_queue(), ^{
hudd.progress = value;
});
});
}
}
hud appears fully to 100%. This is only for my information, I dont have idea how to create background task which calculate something and when he done with e.g. 40% the HUD is refreshing to 40% of his progress. I hope I made ​​myself clear, and if anyone has time to help improve my code, thanks a lot for any answers
In this case, you can solve the problem by decoupling the updating of the counter from the updating of your HUD in your UI. Apple refers to this as "updating the state asynchronously" in WWDC 2012 video Asynchronous Design Patterns with Blocks, GCD, and XPC.
Generally this isn't necessary (most of the time the stuff we're doing asynchronously is slow enough that we don't have problems), but if doing something that is running faster than the UI can hope to keep up with, you create a "dispatch source" for this. I'm going to illustrate it with a UIProgressView, but the same applies to pretty much any UI:
// create source for which we'll be incrementing a counter,
// and tell it to run the event handler in the main loop
// (because we're going to be updating the UI)
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
// specify what you want the even handler to do (i.e. update the HUD or progress bar)
dispatch_source_set_event_handler(source, ^{
self.iterations += dispatch_source_get_data(source);
[self.progressView setProgress: (float) self.iterations / kMaxIterations];
});
// start the dispatch source
dispatch_resume(source);
// now, initiate the process that will update the source
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (long i = 0; i < kMaxIterations; i++)
{
// presumably, do something meaningful here
// now increment counter (and the event handler will take care of the UI)
dispatch_source_merge_data(source, 1);
}
// when all done, cancel the dispatch source
dispatch_source_cancel(source);
});
In my example, iterations is just a long property:
#property (nonatomic) long iterations;
And I defined my kMaxIterations constant as follows:
static long const kMaxIterations = 10000000l;
First off, if you want to delay execution use dispatch_after: Apple Doc since it could be that Clang is optimizing your loop (i.e. by making it not exist).
Within that block call dispatch_sync on the main thread to update the UI, since dispatch_async is not guaranteed to execute 'evenly'. Something like this ought to work...
for (...) {
dispatch_after(<some formula of i>, DEFAULT_PRIORITY, ^{
dispatch_sync(MAIN_QUEUE, ^{ hudd.progress = value });
}
}

Objective-C equivalent of Java's LinkedBlockingQueue<Long>

I'm developing an iOS application with latest SDK and I have to implement a LinkedBlockingQueue with Objective-C.
I have to do something like this code:
public boolean onEvent(final EventArgs e) {
if (e.getClass() != this.eventType) return false;
long now = android.os.SystemClock.uptimeMillis();
long diff = now - this.last;
final long threadExecutionTimeMs = now - lastThreadExecution;
if (executions.remainingCapacity() == 0)
{
executions.poll();
}
executions.add(threadExecutionTimeMs);
...
}
Probably I can use NSMutableArray but I don't know how to emulate blocking, etc.
Any advice?
There's no equivalent AFAIK, you'll need to roll your own.
I think I'd probably use a dispatch semaphore.
Create a class with an instance variable that is the list of items in the queue (NSMutableArray is probably a good candidate). The class should also have an instance variable that is a dispatch semaphore.
In -init initialise the array and the semaphore with an initial count of zero.
Create two methods, one to enqueue an element and one to dequeue an element. The enqueue must also signal the semaphore after adding the element. The dequeue must wait on the semaphore before removing the element.
The code would look something like this:
#implementation MyQueue
{
NSMutableArray* myArray;
dispatch_semaphore_t fd_sema;
}
-(void) enqueue (id) anObject
{
#synchronized(myArray)
{
[myArray addObject: anObject];
}
dispatch_semaphore_signal(fd_sema);
}
-(id) dequeue
{
dispatch_semaphore_wait(fd_sema, DISPATCH_TIME_FOREVER);
id ret = nil;
#synchronized(myArray)
{
ret = [myArray objectAtIndex: 0];
[myArray removeObjectAtIndex: 0];
}
return ret;
}
The semaphore effectively counts the number of objects in the array. dispatch_semaphore_signal() increments the semaphore. dispatch_semaphore_wait() decrements the semaphore unless it's already zero, in which case the thread stops until something else signals the semaphore.
Accesses to the array itself are synchronized because a) there would otherwise be a race condition on removing something from the queue and b) I can't be bothered to look up whether NSMutableArray is thread safe.

Resources