cancel dispatch_after() method? [duplicate]
Asked Answered
F

4

30

Is there a way to cancel dispatch_after() scheduled for some time in future, and haven't fired so far? I'm trying to make something like a scheduler for updates from server, and this method is just like I want, but, I'd love to cancel and re-schedule it at some point. Is it possible at all or I have to fallback and use NSTimer?

Forceps answered 22/1, 2015 at 11:45 Comment(1)
yup. seems like we have duplicates. Wonder if it is possible to merge this questions?Forceps
N
26

There is NO way to prevent a dispatch_block from executing once it has been dispatched to its queue, meaning that your dispatch_after cannot be canceled. Only option is to add in your block a condition to be checked at runtime to prevent execution. ie.

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^ {
if(self.shouldExecuteDispatchBlock)
{ // do your stuff }  });
Neoprene answered 22/1, 2015 at 21:27 Comment(4)
Hmmm so until the next time for execution of this block comes up (and it's set to be cancelled), I guess it would hog all the memory/objects until then. Maybe a problem if your cycles are like 60 minutes? :-)Retirement
Yes and no. The block copy the memory, wich mean if you refere to a int value in your block, this one will be copied and will stay in memory as a duplicate for as long as the block live. But for object, it does not duplicate them, it copy the adress. MEaning if you refere an image, you will have one UIImage object in the heap but the block will only retain a pointer value (8 bytes in arch64) so, no biggie. Of course the image will be retain in memory untill the block goes out, so it won't be clean by arc.Neoprene
I have turned this into a extension for DispatchQueue: github.com/nrbrook/DispatchAfterCancellableVidavidal
Apple introduced dispatch_block_cancel in iOS 8. It asynchronously cancels blocks created with dispatch_block_create. developer.apple.com/reference/dispatch/…Lil
F
19

OK, so, with all answers collected, and possible solutions, seems like the best one for this case (preserving simplicity) is calling performSelector:withObject:afterDelay: and cancelling it with cancelPreviousPerformRequestsWithTarget: call when desired. In my case - just before scheduling next delayed call:

[NSObject cancelPreviousPerformRequestsWithTarget: self selector:@selector(myDelayedMethod) object: self];

[self performSelector:@selector(myDelayedMethod) withObject: self afterDelay: desiredDelay];
Forceps answered 23/1, 2015 at 14:32 Comment(4)
I started down this path, but how does one do it with class methods (which is why I switched to dispatch_after)?Agamic
@Agamic good one. Then you have to wrap those calls into instance, or use dispatch + flag (guess, second would be simpler)Forceps
Does this not require a run loop, however?Fossil
@Fossil it is require a run loop - as any call to the performSelector:afterDelay: selectorsForceps
G
6

For this purpose i used this class:

https://github.com/SebastienThiebaud/dispatch_cancelable_block

you can call a cancel() function to revoke the execution of what's in the block.

Gaulish answered 23/1, 2015 at 15:14 Comment(1)
If you would use it in Swift - you can look it here github.com/katleta3000/CancelBlocksSuprasegmental
G
3

Use a dispatch timer source (that is what dispatch_after uses internally anyway).

A dispatch timer source can be canceled or its timer parameters changed after creation.

Gunpoint answered 22/1, 2015 at 21:16 Comment(1)
Works well for e.g. watchdog timers.Metalanguage

© 2022 - 2024 — McMap. All rights reserved.