Prevent dispatch_after() background task from being executed
Why even use GCD? You could just use an NSTimer
and invalidate it when your app returns to the foregound.
Stop a DispatchQueue that is running on the main thread
You can use DispatchWorkItem
s. They can be scheduled on DispatchQueue
s and cancelled before their execution.
let work = DispatchWorkItem(block: {
self.isShootingOnHold = false
self.shoot()
self.shootingEngine = Timer.scheduledTimer(timeInterval: (Double(60)/Double(self.ratePerMinute)), target: self, selector: #selector(ShootingEnemy.shoot), userInfo: nil, repeats: true)
})
DispatchQueue.main.asyncAfter(deadline: .now() + (delay * Double(isDelayAccounted.hashValue)) + extraDelay, execute: work)
work.cancel()
How to stop a DispatchWorkItem in GCD?
GCD does not perform preemptive cancelations. So, to stop a work item that has already started, you have to test for cancelations yourself. In Swift, cancel
the DispatchWorkItem
. In Objective-C, call dispatch_block_cancel
on the block you created with dispatch_block_create
. You can then test to see if was canceled or not with isCancelled
in Swift (known as dispatch_block_testcancel
in Objective-C).
func testDispatchItems() {
let queue = DispatchQueue.global()
var item: DispatchWorkItem?
// create work item
item = DispatchWorkItem { [weak self] in
for i in 0 ... 10_000_000 {
if item?.isCancelled ?? true { break }
print(i)
self?.heavyWork()
}
item = nil // resolve strong reference cycle of the `DispatchWorkItem`
}
// start it
queue.async(execute: item!)
// after five seconds, stop it if it hasn't already
queue.asyncAfter(deadline: .now() + 5) {
item?.cancel()
item = nil
}
}
Or, in Objective-C:
- (void)testDispatchItem {
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
static dispatch_block_t block = nil; // either static or property
__weak typeof(self) weakSelf = self;
block = dispatch_block_create(0, ^{
for (long i = 0; i < 10000000; i++) {
if (dispatch_block_testcancel(block)) { break; }
NSLog(@"%ld", i);
[weakSelf heavyWork];
}
block = nil;
});
// start it
dispatch_async(queue, block);
// after five seconds, stop it if it hasn't already
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (block) { dispatch_block_cancel(block); }
});
}
Pass DispatchTime as an argument in DispatchAfter
This is what you want in Swift 3+:
let seconds = 2.0
// dispatchTime 2 seconds from now:
let dispatchTime: DispatchTime = DispatchTime.now() + seconds
DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
// code to be executed
}
Related Topics
"Cannot Assign Value of Type 'String' to Type 'Anyobject'", Swift 3, Xcode 8 Beta 6
Bold Part of String in Uitextview Swift
Apply Vertical Alpha Gradient to Uitableview
How to Display Realm Results in Swiftui List
What Is the Advantage of a Lazy Var in Swift
Extend Existing Protocols to Implement Another Protocol with Default Implements
How to Conform to Nscopying and Implement Copywithzone in Swift 2
Nstextfield, Change Text in Swift
Is There a Writetofile Equivalent for Swift 3's 'Data' Type
Swift, How to Play Sound When Press a Button
How to Declare an Inline Function in Swift
Set a Default Value for Uipickerview in Swift
How to Completely Remove Realm Database from iOS
How to Reproduce This Xcode Blue Drag Line