Do action after foreach and dispatch - ios

I am trying to do an action AFTER the foreach and my dispatches. In the for loop when i come across one type of enum, the showAlert will get YES. For showing the allert after all the tasks and loops.
I've tried it, but the allert show up already when the dispatch_sync is still going. How can i solve this problem?
EDIT:
Problem overview: The problem is then that the UIAlertView already comes up before the loops are done, so i don't have any value set (For loop does this of my randomInt) and my AlertView takes this int. So That's why i want to wait until all loops/foreach are done in the method and then let the UIAlertView come up, so i have 100% the integer that i needed.
Code:
-(void)animateRolling{
[[self firstLabelOutlet] setHidden:YES];
long index;
int randomIndex;
__block BOOL showAllert = NO;
randomIndex = arc4random() % 20;
if (randomIndex == 8) {
[self showAd];
}
for(detailDice* cell in [[self dobbelstenenCollection] visibleCells]){
NSIndexPath *indexPath = [[self dobbelstenenCollection] indexPathForCell:cell];
if ([arrayDobbel count] >= 3) {
index = 2 * indexPath.section + indexPath.row;
}else{
index = 1 * indexPath.section + indexPath.row;
}
if ([cell isHidden]) {
[cell setHidden:NO];
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i <= 10; i++) {
dispatch_sync(dispatch_get_main_queue(), ^{
if ([[arrayDobbel objectAtIndex:index] diceSoort] == ENUMHoelangDobbel) {
randomInt = ((arc4random() % ([self maxduration] - 1 + 1 )) + 1.0);
[[cell detaildiceLabel] setText:[NSString stringWithFormat:#"Seconds \n %i", (int)randomInt]];
showAllert = YES;
}else if ([[arrayDobbel objectAtIndex:index] diceSoort] == ENUMOgen){
[[cell detailDiceImage] setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", ( (arc4random() % (6-1+1)) + 1 )]]];
}else{
[[cell detaildiceLabel] setText: [[arrayDobbel objectAtIndex:index] returnRandomOptie]];
}
});
[NSThread sleepForTimeInterval:0.05];
}
});
}
if (showAllert) {
[self showDurationAlert];
}
}
Kind Regards!

Create group with dispatch_group_create.
Use dispatch_group_async instead dispatch_async.
And path yours block, that should be performed after foreach and dispatch in dispatch_group_notify.
All is simple.

I would suggest to refactor your code through making animateRolling an asynchronous method, and the invocation of showDurationAlert a "continuation":
typedef void (^completion_t)(BOOL showAlert);
-(void)animateRollingWithCompletion:(completion_t)completion;
and use it as follows:
[self animateRollingWithCompletion:^(BOOL showAlert){
if (showAlert) {
dispatch_async(dispatch_get_main_queue(), ^{
[self showDurationAlert];
});
}
}];
I would also suggest to utilize a NSTimer or better a dispatch timer (dispatch_source_t) to implement the method animateRollingWithCompletion.
You can create and use dispatch timer as follows:
// Create a timer:
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// Define the handler block:
dispatch_source_set_event_handler(timer, ^{
...
});
// Start the timer (here, as a one shot timer):
uint64_t leeway = 0; // allowed maximum delay
uint64_t interval = delay_in_secs * NSEC_PER_SEC;
dispatch_source_set_timer(
timer,
dispatch_time(DISPATCH_TIME_NOW, interval), // time *when* to start
DISPATCH_TIME_FOREVER, // interval, DISPATCH_TIME_FOREVER for "one shot"
leeway // maximum allowed delay
);
dispatch_resume(timer);
// Cancel a timer:
dispatch_source_cancel(timer);
In order to start a periodic timer whose handler will be called at the start of each period, you might write:
dispatch_source_set_timer(
timer,
dispatch_time(DISPATCH_TIME_NOW, 0),
interval,
leeway);
dispatch_resume(timer);
With this knowledge, I think it's possible to craft a handler block, which is driven by a periodic timer. The block retrieves parameters form an array and executes your core methods (e.g. setting up the cells) on the main thread.
When all parameters have been retrieved and the work has been finished, the block may call dispatch_after() with the specified interval (or test for the "finished" condition at the start of a new period) which eventually calls the completion block provided as parameter in method animateRollingWithCompletion and defined by the call-site (which in turn brings up the Alert View).

Related

Break point in XCTestCase can't be reached

I have a method that requests data from network , and I want to unit test it. But when I set a break point in the test case , the break point won't get there.
The method to be tested:
- (void)requestSuperDecisionDataWithCompletionHandler:(void (^)(NSArray *result))callBack {
static vector<vector<string>> arr;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//request data from network
IceNetwork::Init()->GetSuperDecision(arr);
if (arr.size() != kSuperDecisionDataCount) {
callBack(nil);
} else {
NSArray *convertedData = [self convertCArrayToNSArrayWithCArray:arr];
dispatch_async(dispatch_get_main_queue(), ^{
callBack(convertedData);
});
}
});
}
Test case:
- (void)testThatRequestSuperDecisionDataShouldReturnZeroOr14Items
{
//super decision
[_requestManager requestSuperDecisionDataWithCompletionHandler:^(NSArray *result) {
//set a break point here
int dataCount = result.count;
XCTAssert(0 == dataCount || 16 == dataCount, #"should have 0 or 16 items");
}];
}
In that case, because that is an asynchronous operation, the test is completing before the completion handler is called. With that in mind, there are two methods you can use to do this, both involving essentially waiting for the completion handler to be called before allowing your tests to complete.
The first method utilizes new APIs available in the Xcode 6 beta.
Method #1 - New XCTestExpectation APIs
- (void)testThatRequestSuperDecisionDataShouldReturnZeroOr14Items {
// set an expectation for your test, in this case we expect the completion handler to be
// called within a reasonable amount of time
XCTestExpectation *expectation = [self expectationWithDescription:#"The completion handler should be called."];
// start whatever asyncronous task you want to do
[_requestManager requestSuperDecisionDataWithCompletionHandler:^(NSArray *result) {
// here we handle the actual meat of the tests
int dataCount = result.count;
XCTAssert(0 == dataCount || 16 == dataCount, #"should have 0 or 16 items");
// now we can tell the test that our expectation has been fulfilled, which will
// allow the test to complete
[expectation fulfill];
}];
// we want the test to wait for the expectations for some period of time, but we also
// want to establish some sort of timeout interval where the test will basically be
// terminated and the result will be a timeout failure, you can set the timeout to whatever
// interval you want for each case, and optionally provide a handler to clean up anything
[self waitForExpectationsWithTimeout:2 handler:nil];
}
A second option is available if you are running Xcode 5 and thus don't have access to the new APIs, but is essentially the same process, just slightly more involved.
Method #2 - Do-it-yourself Async Testing
- (void)testThatRequestSuperDecisionDataShouldReturnZeroOr14Items {
// create a BOOL flag that will keep track of whether or not we're still
// waiting for the completion block to be called
__block BOOL waitingForBlock = YES;
// start whatever asyncronous task you want to do
[_requestManager requestSuperDecisionDataWithCompletionHandler:^(NSArray *result) {
// here we handle the actual meat of the tests
int dataCount = result.count;
XCTAssert(0 == dataCount || 16 == dataCount, #"should have 0 or 16 items");
// now we can update the waitingForBlock variable to NO as we are no
// longer waiting for it to complete
waitingForBlock = NO;
}];
// kill the test after some specified delay if we haven't completed yet - just in case something
// happens with the test where the handler will never be called, at least the test will complete
// and we will know we timed-out.
NSTimeInterval seconds = 3;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) {
XCTFail(#"'testThatRequestSuperDecisionDataShouldReturnZeroOr14Items' failed due to timeout.");
waitingForBlock = NO;
});
// loop while we are waiting for the completion handler to finish
while (waitingForBlock) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
}

iOS - How to Interrupt an Asynchronous Background Operation, After a Set Amount of Time

Note: please don't comment on whether or not this is a good idea ;) I'm just experimenting so want to see how it pans out...
I've got a UITextView and some inputText, which I'm going to set as the text for the UITextView.
I want to try visually drip-feeding the inputText to the text view, say, one letter at a time or one word at a time.
For example (without any consideration for the drip-feed delay or thread of execution):
for (NSInteger i = 0; i < inputText.length; i++) {
NSString* charAtIndex = [text substringWithRange:NSMakeRange(i, 1)];
_textView.text = [NSString stringWithFormat:#"%#%#", _textView.text, charAtIndex];
}
So, to build in a delay and see the characters (or words) added, one at a time, I can do the following:
dispatch_queue_t backgroundQueue = dispatch_queue_create("background_queue", 0);
for (NSInteger i = 0; i < inputText.length; i++) {
dispatch_async(backgroundQueue, ^{
[NSThread sleepForTimeInterval:0.01f];
NSString* charAtIndex = [inputText substringWithRange:NSMakeRange(i, 1)];
dispatch_async(dispatch_get_main_queue(), ^{
_textView.text = [NSString stringWithFormat:#"%#%#", _textView.text, charAtIndex];
});
});
}
That works fine, and as expected, the for loop is queueing up a bunch of asynchronous operations on the background queue.
But I want the above to take place as part of a larger, single synchronous visualisation. Before adding this code to drip-feed the UITextView, I simply set the text in the UITextView and then animated the current view (including the UITextView) off-screen. All on the main thread. The user would tap a button, see the whole text appear and then the view starts to animate off the screen, right away. The user then moves on to the next step in the workflow.
I'm trying the drip-feed visualisation to give the impression of the text view being populated one character (or word) at a time, but I don’t want the user to have to wait until every last character/word has been added. So, I’d like to see the UITextView being populated for say, 0.3 or 0.5 of a second, before starting the animation that animates the view (including the UITextView) off-screen…
So it’s kinda like this:
dispatch_queue_t backgroundQueue = dispatch_queue_create("background_queue", 0);
for (NSInteger i = 0; i < inputText.length; i++) {
dispatch_async(backgroundQueue, ^{
[NSThread sleepForTimeInterval:0.01f];
NSString* charAtIndex = [inputText substringWithRange:NSMakeRange(i, 1)];
dispatch_async(dispatch_get_main_queue(), ^{
_textView.text = [NSString stringWithFormat:#"%#%#", _textView.text, charAtIndex];
});
});
}
// ...
// Animate the view (including the UITextView) off screen
// ...
// User continues with the main workflow
Right now, with all that drip-feeding happening asynchronously in the background, once I add in the code to animate the view out of the way, you miss the visual drip-feed. The main thread runs right through to animating the view off screen.
I’m not sure how to achieve what I want?
Do I interrupt the above loop in some way? Check for a flag that’s updated from another thread?
I can’t put the wait on the main thread - because it will prevent the drip-feed updates to the UITextView…
Any suggestions?
You can delay the animation to "give" time to the drip-feed, like this:
double delayInSeconds = 0.5f;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// ...
// Animate the view (including the UITextView) off screen
// ...
});
This way the user will see the drip-feed animation for 0.5s. This is the GCD version but you can use the method too:
[UIView animateWithDuration:0.3f // Set your duration here
delay:0.5f
options:UIViewAnimationOptionCurveEaseOut // Choose the right option here
animations:^{
// Do your animations here.
}
completion:^(BOOL finished){
if (finished) {
// Do your method here after your animation.
}
}];
You're better off not queuing up loads of tasks and then setting up another delayed animation. Yes, it will work, but it isn't so maintainable and doesn't cater well for other situations in the future.
Instead, think about using an NSTimer and a couple of instance variables (which could be wrapped up in another class to keep everything clean and tidy). The instance variable is basically the current progress (i from your loop). Each time the timer fires, check i - if the text animation isn't complete, use i to substring and update the UI. If the text animation is complete, invalidate the timer and start the final view animation.
In this way the logic is organised, easily understandable, reusable and cancellable.
Based on what J. Costa suggested, I've stayed with the Grand Central Dispatch approach. The piece that I was missing (and I'm not sure that I explained this requirement very well) is the access to a shared resource.
In the following code, I've structured it so:
BOOL shared resource to indicate whether or not the drip-feed should continue
create a serial queue for reading from / writing to that shared resource
the drip-feed happens on a background queue and additionally uses the serial queue to check if it should continue
the delayed view animation is setup using dispatch_after, and when it occurs it uses the serial queue to signal the drip-feeding should stop
Code:
NSString* inputText = #"Some meaningful text...";
dispatch_queue_t serialQueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);
// the shared resource
_continueDripFeed = YES;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < inputText.length; i++) {
__block BOOL keepGoing = NO;
dispatch_sync(serialQueue, ^{
// read from the shared resource
keepGoing = _continueDripFeed;
});
if (keepGoing) {
[NSThread sleepForTimeInterval:0.02f];
NSString* charAtIndex = [inputText substringWithRange:NSMakeRange(i, 1)];
dispatch_async(dispatch_get_main_queue(), ^{
_textView.text = [NSString stringWithFormat:#"%#%#", _textView.text, charAtIndex];
});
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
_textView.text = inputText;
});
break;
}
}
});
double delayInSeconds = 0.5f;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
dispatch_sync(serialQueue, ^{
// update the shared resource
_continueDripFeed = NO;
});
[self animateTextViewToFrame:_offScreenFrame];
// Continue with workflow...
});

Using dispatch_after to do something relative to when something else finishes

Here is what I need to do:
Something like:
for int i = 0; i < 3; i++)
call method #selector(spin) with delay 0.1
//once this is done (e.g., in 0.3 seconds)
call method #selector(move) with delay 1 second.
What I need is to pile on events, and have other events start relative to when the previous one finishes.
So something like:
wait 100ms //total time = 100ms
spin
wait 100ms //total time = 200ms
spin
wait 100ms //total time = 300ms
spin
wait 1000ms //total time = 1300ms
move
Is something like this possible with dispatch_after? If so could someone give me an example? I cannot seem to find one for this case.
Note, none of this should cause the UI thread to wait / block.
Thanks
You can use NSThread's sleepForTimeInterval method. The following code will block the thread sleepForTimeInterval is running on.
dispatch_queue_t yourThread = dispatch_queue_create("com.xxx.queue", nil);
dispatch_async(yourThread, ^
{
[NSThread sleepForTimeInterval:10.0];
dispatch_async(dispatch_get_main_queue(), ^
{
});
});
This should point you to the right direction.
I personally think that you're trying to achive the goal in wrong way. When you deal with asynchronous process you should use blocks
Based on what your request :
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self performSelector:#selector(move:) withObject:[NSNumber numberWithInt:0] afterDelay:2];
}
-(void) move:(NSNumber *)i {
int val = [i intValue] + 1;
//do what you need here
if (val < 3) [self performSelector:#selector(move:) withObject:[NSNumber numberWithInt:val] afterDelay:2];
}
But Of course if you need to process a lot of data, use other thread instead...
dispatch_group_t group = dispatch_group_create();
dispatch_time_t timeNow = DISPATCH_TIME_NOW;
for (int i = 0; i < 3; ++i)
{
dispatch_group_enter(group);
dispatch_after(dispatch_time(timeNow, i*100), queue, ^(){
[self spin];
dispatch_group_leave(group);
});
}
dispatch_group_notify(group, queue, ^(){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1000), queue, ^(){
[self move];
});
});

Wait for many asynchronous calls to perform callback

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

Assigning values within block with GCD

Im executing a thread that keeps looking for updates from a web site. It should be possible to set the refresh rate in some view.
The update thread keeps checking for a updated interval. But I would like to avoid race conditions. (Do I even have to worry for this with GCD?)
//This variable is used to avoid race conditions, refreshRate is a instance variable
int threadRefreshRate = refreshRate;
BOOL autoRefresh = YES;
dispatch_async(autoUpdateQueue, ^ {
while(YES){
NSLog(#"Runs autoupdate thread");
dispatch_async(dispatch_get_main_queue(), ^{
if(autoRefresh){
[self checkForUpdate];
//Trying to set thread variable to avoid race condition
threadRefreshRate = refreshRate;
}
else
NSLog(#"Should not auto refresh");
});
sleep(threadRefreshRate);
}
});
I tried to implement this code. However it doesn't work to assing the 'thread'-variable within a block.
For the kind of code you have given, i would use the timer events raised on the queue instead doing explicit sleep in the code. This way you dont have to worry about the race conditions etc..
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (!timer) return;
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), refreshRate * NSEC_PER_SEC, 5 * NSEC_PER_SEC);
dispatch_source_t timer = self.timer;
//initialize self to blockSelf with __block
self.timerAction = ^{
[blockSelf checkForUpdate];
};
dispatch_source_set_event_handler(timer, timerAction);
dispatch_resume(timer);
When the autoRefresh is set to NO, you can cancel it by
dispatch_source_cancel(timer);

Resources