取消 Swift 中的定时事件?

Cancel a timed event in Swift?

我想 运行 事件后 10 秒内的代码块,但我希望能够取消它,这样如果在这 10 秒之前发生了什么,代码将不会 运行 10 秒后。

我一直在使用这个,但它是不可取消的:

static func delay(delay:Double, closure:()->()) {
  dispatch_after(
    dispatch_time(
      DISPATCH_TIME_NOW,
      Int64(delay * Double(NSEC_PER_SEC))
    ),
    dispatch_get_main_queue(), closure
  )
}

我怎样才能做到这一点?

这应该有效:

var doIt = true
var timer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: Selector("doSomething"), userInfo: nil, repeats: false)

//you have now 10 seconds to change the doIt variable to false, to not run THE CODE

func doSomething()
{
    if(doIt)
    {
        //THE CODE
    }
    timer.invalidate()
}

试试这个(Swift 2.x,请参阅下面大卫对 Swift 3 的回答):

typealias dispatch_cancelable_closure = (cancel : Bool) -> ()

func delay(time:NSTimeInterval, closure:()->()) ->  dispatch_cancelable_closure? {

    func dispatch_later(clsr:()->()) {
        dispatch_after(
            dispatch_time(
                DISPATCH_TIME_NOW,
                Int64(time * Double(NSEC_PER_SEC))
            ),
            dispatch_get_main_queue(), clsr)
    }

    var closure:dispatch_block_t? = closure
    var cancelableClosure:dispatch_cancelable_closure?

    let delayedClosure:dispatch_cancelable_closure = { cancel in
        if let clsr = closure {
            if (cancel == false) {
                dispatch_async(dispatch_get_main_queue(), clsr);
            }
        }
        closure = nil
        cancelableClosure = nil
    }

    cancelableClosure = delayedClosure

    dispatch_later {
        if let delayedClosure = cancelableClosure {
            delayedClosure(cancel: false)
        }
    }

    return cancelableClosure;
}

func cancel_delay(closure:dispatch_cancelable_closure?) {
    if closure != nil {
        closure!(cancel: true)
    }
}

// usage
let retVal = delay(2.0) {
    println("Later")
}
delay(1.0) {
    cancel_delay(retVal)
}

来自 Waam 的评论:dispatch_after - GCD in swift?

我在一些项目中使用了@sas 的方法,不知何故这不再有效,也许在 Swift 2.1.1 之后发生了一些变化。值复制而不是指针?

对我来说最简单的解决方法是:

var canceled = false
delay(0.25) {
  if !canceled {
    doSomething()
  }
}

Swift 3 有 DispatchWorkItem:

let task = DispatchWorkItem { print("do something") }

// execute task in 2 seconds
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2, execute: task)

// optional: cancel task
task.cancel()

更新 Swift 3.0

设置执行选择器

perform(#selector(foo), with: nil, afterDelay: 2)

foo 方法将在 2 秒后调用

 func foo()
 {
         //do something
 }

取消挂起的方法调用

 NSObject.cancelPreviousPerformRequests(withTarget: self)

出于某种原因,NSObject.cancelPreviousPerformRequests(withTarget: self) 对我不起作用。我想到的解决方法是提出我允许的最大循环次数,然后使用该 Int 来控制该函数是否被调用。

然后我可以在我的代码中的任何其他地方设置 currentLoop 值并停止循环。

//loopMax = 200
var currentLoop = 0

func loop() {      
  if currentLoop == 200 {      
     //do nothing.
  } else {
     //perform loop.

     //keep track of current loop count.
     self.currentLoop = self.currentLoop + 1

     let deadline = DispatchTime.now() + .seconds(1)
     DispatchQueue.main.asyncAfter(deadline: deadline) {

     //enter custom loop parameters
     print("i looped")

     self.loop()
   }

}

然后您可以在代码的其他地方

func stopLooping() {

    currentLoop = 199
    //setting it to 199 allows for one last loop to happen. You can adjust based on the amount of loops you want to be able to do before it just stops. For instance you can set currentLoop to 195 and then implement a fade animation while loop is still happening a bit.
}

实际上它真的很有活力。例如,您可以查看 currentLoop == 123456789,它会无限地(几乎)运行,直到您在代码中的其他地方将其设置为该值。或者你甚至可以将它设置为 String() 或 Bool(),如果你的需求不像我的那样基于时间。

您需要这样做:

class WorkItem {

private var pendingRequestWorkItem: DispatchWorkItem?

func perform(after: TimeInterval, _ block: @escaping VoidBlock) {
    // Cancel the current pending item
    pendingRequestWorkItem?.cancel()
    
    // Wrap the request in a work item
    let requestWorkItem = DispatchWorkItem(block: block)
    
    pendingRequestWorkItem = requestWorkItem

    DispatchQueue.main.asyncAfter(deadline: .now() + after, execute: 
    requestWorkItem)
}
}

// Use

lazy var workItem = WorkItem()

private func onMapIdle() {

    workItem.perform(after: 1.0) {

       self.handlePOIListingSearch()
    }
}

参考资料

Link swiftbysundell

Link git