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?
-
yup. seems like we have duplicates. Wonder if it is possible to merge this questions? – Oleg Shanyuk Jan 23 '15 at 14:36
4 Answers
There is NO way to prevent a dispatch_block from executing once it has been dispatch to it's 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 } });

- 3,408
- 1
- 19
- 20
-
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? :-) – Jonny Jul 03 '15 at 05:33
-
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. – Florian Burel Jul 09 '15 at 12:39
-
I have turned this into a extension for DispatchQueue: https://github.com/nrbrook/DispatchAfterCancellable – Nick Sep 13 '16 at 00:29
-
4Apple introduced `dispatch_block_cancel` in iOS 8. It asynchronously cancels blocks created with `dispatch_block_create`. https://developer.apple.com/reference/dispatch/1431058-dispatch_block_cancel?language=objc – Heath Borders Mar 03 '17 at 03:54
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];

- 1,296
- 2
- 15
- 26
-
I started down this path, but how does one do it with class methods (which is why I switched to `dispatch_after`)? – Olie Dec 20 '15 at 00:13
-
@Olie good one. Then you have to wrap those calls into instance, or use dispatch + flag (guess, second would be simpler) – Oleg Shanyuk Dec 21 '15 at 00:27
-
-
@MarcWan it is require a run loop - as any call to the `performSelector:afterDelay:` selectors – Oleg Shanyuk May 11 '18 at 10:00
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.

- 2,468
- 2
- 34
- 51
-
3If you would use it in Swift - you can look it here https://github.com/katleta3000/CancelBlocks/ – katleta3000 Feb 16 '15 at 14:56
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.

- 3,651
- 17
- 19