是否取消Swift中的定时事件?

是否取消Swift中的定时事件?,swift,events,Swift,Events,我希望在事件发生后的10秒内运行一段代码,但我希望能够取消它,这样,如果在这10秒钟之前发生了什么事情,代码在经过10秒钟后将不会运行 我一直在使用这个,但它不可取消: static func delay(delay:Double, closure:()->()) { dispatch_after( dispatch_time( DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)) ),

我希望在事件发生后的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的回答):


Waam在这里的评论是:

我在一些项目中使用了@sas的方法,不知怎么的,这已经不起作用了,可能在Swift 2.1.1之后有所改变。值复制而不是指针

对我来说,最简单的变通方法是:

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

Swift 3有
调度工作项

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(with target: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 currently pending item
    pendingRequestWorkItem?.cancel()

    // Wrap our request in a work item
    let requestWorkItem = DispatchWorkItem(block: block)

    pendingRequestWorkItem = requestWorkItem

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

// to use

lazy var workItem = WorkItem()

private func onMapIdle() {

    workItem.perform(after: 1.0) {

       self.handlePOIListingSearch()
    }
}
参考资料



这实际上并没有取消计时器。它只是让计时器中的代码停止运行。这不是你想要的吗?您可以使用变量“doIt”取消它,因此如果您不想执行代码,只需将其设置为false..否。无论发生什么情况,doSomething函数仍然运行。这远没有我刚才接受的答案有用。例如,假设您启动了一个计时器,将变量设置为false以“取消”,然后启动另一个计时器将变量设置回true。该代码将在第一个计时器完成时执行。我刚刚尝试在最新版本的Xcode中使用它,它似乎工作得很好。有人可以将其更新为Swift 3.0吗?我一直使用到2.3版本,但在更新到Swift 3.0之后,编译器会抱怨将闭包设置为零。请参见上文,但请注意,它未经测试,可能不是使用Swift 3.0的最佳方法。让我知道它是否有效,我会更新它。对我来说,swift 3代码不起作用。我必须将闭包声明为@escaping()->void您是否尝试了我上周添加的Swift 3版本?我将对其进行编辑,以使其更清晰。在Swift 3上对我来说效果不好。我注意到,
asyncAfter:execute:
有时不遵守截止日期:clouse的执行比预期的要晚@戴维德劳森的回答还行。在《雨燕3》中,这似乎是一个恰当的方法。应该是可以接受的答案。这要看情况而定。它“很好”而且相当干净,但是您必须跟踪一个变量以便稍后取消它,如果您稍后在其他函数中取消它,则可能是一个实例变量。
NSObject
函数为您处理,因此无需记账。(当然,如果您谈论的是纯Swift,那么
NSObject
方式不是一个选项。)请注意,task.cancel()将取消所有挂起的DispatchWorkItem事件,并阻止任何未来的DispatchWorkItem事件运行。如果您试图创建类似于打开和关闭的重复轮询计时器的内容,则需要在每次“取消”任务后要“重新启动”计时器时创建新的DispatchWorkItem。本质上,task.cancel()使DispatchWorkItem“无效”,不能再次使用。我怀疑苹果在内部为DispatchWorkItem设置了isCancelled标志,但我没有找到一种“清除”该标志以便重用的方法。我认为这是一个“特性”。@Tejas您可以将DispatchWorkItem子类化以覆盖其构造函数,这样您就可以在每次创建新对象时设置可执行代码。这样,在调用.cancel()之后,您可以用新对象替换旧对象,新对象将继续工作。这就是我使用的对象,我还没有遇到任何问题。简短,甜蜜,直截了当!老实说,这是我比较喜欢的处理方式。你不必保留任何状态来取消它
NSObject
会为您处理它,因此您可以在不做任何额外记账的情况下取消和延迟选择器。这依赖于ObjC运行时,所以一点也不快速。从技术上讲,是的,您是对的。幸运的是,目前99%的Swift开发都是针对macOS/iOS的,所以它现在可以工作了。回答得很好!我使用此方法合并多个观察者调用。我不希望观察员代码不断重复发射。使用
cancelPreviousPerformRequests
对我来说非常有效。
class WorkItem {

private var pendingRequestWorkItem: DispatchWorkItem?

func perform(after: TimeInterval, _ block: @escaping VoidBlock) {
    // Cancel the currently pending item
    pendingRequestWorkItem?.cancel()

    // Wrap our request in a work item
    let requestWorkItem = DispatchWorkItem(block: block)

    pendingRequestWorkItem = requestWorkItem

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

// to use

lazy var workItem = WorkItem()

private func onMapIdle() {

    workItem.perform(after: 1.0) {

       self.handlePOIListingSearch()
    }
}