Is it possible to make this "clearer" or "better" ?
Any solution is welcome, even thought i got a right answer.
The problem is dispatch_after() with popTime==0 is still giving time to the main thread to make some UI changes. The following code is sometimes called from a background thread.
-(void)methodCalledFromSomeThread{
if (delayInSeconds) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// Updating some UI like
});
}else{
dispatch_async(dispatch_get_main_queue(), ^{
// Updating some UI like
});
}
}
dispatch_async on the main queue will also queue the code to run on the next runloop, giving time for the system to update the UI. You can check like so:
void (^block)() = ^{
[self.tableView reloadData];
};
if (delayInSeconds) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), block);
}else if([NSThread isMainThread]) {
block();
}
else {
dispatch_async(dispatch_get_main_queue(), block);
}
Related
have written a method for updating which will call itself again and again when we start that activity, as mentioned below. I wants to stop this method function once we get an output (Now, it keeps running even if we get output). Please let me know if there is any possibility to do it.
-(void)reloadMethod{
[self downloadProgressAudio];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self reloadMethod];
});
}
If you need to keep calling this method until you get an output, the you need to include that test, and only call the function again if you don't have any output.
if !outputDataExists {
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self reloadMethod];
});
}
You can use timer here
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(reloadMethod)
userInfo:nil
repeats:YES];
Once you are done with your repeated execution then do this
[timer invalidate];
timer = nil;
Here is the case:
There are more than 3 async tasks. Each one can only be executed after the completion of the previous one. Yes, must be after the completion of the previous one. That's why dispatch group might not be good for this case because it doesn't care about the 'ordering' quite much. What I'm looking for is a good way of writing such code without too much nesting - just similar to what 'promisekit' does to break the nested block.
I also saw some people suggesting 'serial queue'. Well I tried using the code below:
- (void) asyncMethod1WithCompletion: (void(^)())completion
{
int64_t delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(#"async 1 finished");
completion();
});
}
- (void) asyncMethod2WithCompletion: (void(^)())completion
{
int64_t delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(#"async 2 finished");
completion();
});
}
And I call those methods using serial queue:
dispatch_queue_t serialQueue = dispatch_queue_create("testqueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(#"async1 started");
[self asyncMethod1WithCompletion:^{
}];
});
dispatch_sync(serialQueue, ^{
NSLog(#"async2 started");
[self asyncMethod2WithCompletion:^{
}];
});
If everything works well, async task 2 should start executing after 5 seconds which is the completion time of async task one
However, the result is not as what I expected:
2016-09-14 18:59:39.853 SerialQueueTest[32002:2292385] async1 started
2016-09-14 18:59:39.854 SerialQueueTest[32002:2292385] async2 started
2016-09-14 18:59:41.854 SerialQueueTest[32002:2292385] async 2 finished
2016-09-14 18:59:45.353 SerialQueueTest[32002:2292385] async 1 finished
Did I write the code wrong? Is there any native way of writing lesser nesting code? If there are 5-6 async tasks and each relies on the result of each other, the code would be very massy. I would even think of one very case: a common login function which also integrates some 3rd party accounts login.
I'm also thinking of using dispatch group in a different way: 1. enter the group and finish the first async task and exit group. 2. inside the dispatch_group_notify block, enter the dispatch group again for second async task. 3. Outside the disptach_group_notify block, calling the second async task and exit the group again. 4. write the second dispatch_group_notify for notifying the completion of the second task.
- (dispatch_group_t) asyncMethod1
{
NSLog(#"async1 started");
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
int64_t delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(#"async 1 finished");
dispatch_group_leave(group);
});
return group;
}
- (dispatch_group_t) asyncMethod2
{
NSLog(#"async2 started");
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
int64_t delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(#"async 2 finished");
dispatch_group_leave(group);
});
return group;
}
And using:
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
dispatch_group_notify([self asyncMethod1], queue, ^{
[self asyncMethod2];
});
});
dispatch_group_t looks similar there like promise.
As you can see, you can even use concurrent queue.
You can use NSOperationQueue here.
Create n block operations(NSBlockOperation), where n is the number of async operations you have.
Add them in the NSOperationQueue in the order you want them to be executed in.
Set maxConcurrentOperationCount of NSOperationQueue to 1 to make the queue as serial.
I have a simple iOS app which needs to delay a certain block of code before it can be run.
I am using dispatch_time method to achieve this and it works very nicely. Just one question though, dispatch_time takes in an integer, so the time that you can set are only allowed to be whole numbers such as 1.0 seconds or 3.0 seconds, etc....
I want to set it to delay by 0.5 seconds. How can I do this? Below is my code:
int64_t time_delay = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, time_delay * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
/*
my block of code....
*/
});
Don't use a int64_t but a CGFloat. It will work fine.
CGFloat time_delay = 0.5f;
dispatch_time() takes nanoseconds as second parameter. Remove NSEC_PER_SEC from second parameter and your delay will be in nanoseconds
double delay = 2.25;
// 2250000000 = 2.25 * 1000000000
dispatch_time_t offset_time = delay * NSEC_PER_SEC;
// current mach_absolute_time + offset
dispatch_time_t fire_time = dispatch_time(DISPATCH_TIME_NOW, offset_time);
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.25), dispatch_get_main_queue(), ^{
dispatch_after(fire_time, dispatch_get_main_queue(), ^{
NSLog(#"time %llu", t);
});
Is there a good way to call an external method after a set time limit for completing the long process outlined below? I would like the long process to stop trying after a set interval and call a method to try something else and wrap up the request.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//// LONG PROCESS
dispatch_async(dispatch_get_main_queue(), ^{
//// RESULTS PROCESS
});
});
In order to "kill" the process that's running your block, you'll have to check a condition. This will allow you to do cleanup. Consider the following modifications:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
BOOL finished = NO;
__block BOOL cancelled = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
if (!finished) {
cancelled = YES;
}
});
void (^cleanup)() = ^{
// CLEANUP
};
//// LONG PROCESS PORTION #1
if (cancelled) {
cleanup();
return;
}
//// LONG PROCESS PORTION #2
if (cancelled) {
cleanup();
return;
}
// etc.
finished = YES;
dispatch_async(dispatch_get_main_queue(), ^{
//// RESULTS PROCESS
});
});
In the ////Long Process change a boolean value (like BOOL finished) to true when finished.
After the call to dispatch_async(...) you typed here, add this:
int64_t delay = 20.0; // In seconds
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^(void){
if (!finished) {
//// Stop process and timeout
}
});
In this way, after 20 seconds (or any time you want) you can check if the process is still loading and take some provisions.
I've used this method for Swift:
let delay = 0.33 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
//// RESULTS PROCESS
}
I need a special version of dispatch_after. I need it to pass a parameter to the anonymous function, and the anonymous function checks if the parameter and a class variable match before executing.
I already have it working like this:
double delayInSeconds = 5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
int mod = self.invalidationCount;
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if(mod == self.invalidationCount)
[self doAction:speed accelTime:accel];
});
However, this is dirty as is. I have to copy this chunk around everywhere I need it in my class.
Here is what I would like:
Something along the lines of:
double delayInSeconds = 5;
dispatch_after_safe(delayInSeconds,self, ^(void){
[self doAction:speed accelTime:accel];
});
or if possible, in message form so I don't have to pass self, but thats no issue...
Essentially, dispatch_after_safe should do something like:
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
int mod = parameterSelf.invalidationCount;
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if(mod == parameterSelf.invalidationCount)
//execute the parameter block
});
}
Essentially, I need a wrapper for what I am currently doing, but I'm new to objective C and I'm not quite sure syntactically how to do something like this.
The whole point of doing this is, the user can press Back which should halt any queued future events.
Thanks
Do you mean something like this:
void dispatch_after_safe(NSTimeInterval delay, id pmself, dispatch_block_t block) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC);
int mod = pmself.invalidationCount;
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if (mod == pmself.invalidationCount) {
block();
}
});
}
(I recommend replacing the type id with whatever class of object it is supposed to be used with.)