我怎样才能消除方法调用?

我正在尝试使用UISearchView来查询谷歌的地方。 这样做,在我的UISearchBar文本更改调用,我正在向谷歌地方的请求。 问题是我宁愿去掉这个呼叫,每250毫秒只请求一次,以避免不必要的networkingstream量。 我宁愿不自己写这个function,但如果我需要,我会。

我发现: https : //gist.github.com/ShamylZakariya/54ee03228d955f458389 ,但我不太清楚如何使用它:

 func debounce( delay:NSTimeInterval, #queue:dispatch_queue_t, action: (()->()) ) -> ()->() { var lastFireTime:dispatch_time_t = 0 let dispatchDelay = Int64(delay * Double(NSEC_PER_SEC)) return { lastFireTime = dispatch_time(DISPATCH_TIME_NOW,0) dispatch_after( dispatch_time( DISPATCH_TIME_NOW, dispatchDelay ), queue) { let now = dispatch_time(DISPATCH_TIME_NOW,0) let when = dispatch_time(lastFireTime, dispatchDelay) if now >= when { action() } } } } 

这是我尝试使用上述代码的一件事情:

 let searchDebounceInterval: NSTimeInterval = NSTimeInterval(0.25) func findPlaces() { // ... } func searchBar(searchBar: UISearchBar!, textDidChange searchText: String!) { debounce( searchDebounceInterval, dispatch_get_main_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT), self.findPlaces ) } 

由此产生的错误是Cannot invoke function with an argument list of type '(NSTimeInterval, $T5, () -> ())

我如何使用这个方法,或者有更好的方法在iOS / Swift中做到这一点。

把它放在你的文件的最高层 ,以免自己搞糊涂Swift的有趣的参数名称规则。 请注意,我已经删除了#以便现在没有任何参数具有名称:

 func debounce( delay:NSTimeInterval, queue:dispatch_queue_t, action: (()->()) ) -> ()->() { var lastFireTime:dispatch_time_t = 0 let dispatchDelay = Int64(delay * Double(NSEC_PER_SEC)) return { lastFireTime = dispatch_time(DISPATCH_TIME_NOW,0) dispatch_after( dispatch_time( DISPATCH_TIME_NOW, dispatchDelay ), queue) { let now = dispatch_time(DISPATCH_TIME_NOW,0) let when = dispatch_time(lastFireTime, dispatchDelay) if now >= when { action() } } } } 

现在,在您的实际课程中,您的代码将如下所示:

 let searchDebounceInterval: NSTimeInterval = NSTimeInterval(0.25) let q = dispatch_get_main_queue() func findPlaces() { // ... } let debouncedFindPlaces = debounce( searchDebounceInterval, q, findPlaces ) 

现在debouncedFindPlaces是一个你可以调用的函数,除非你最后一次调用它,否则你的findPlaces将不会被执行。

Swift 3版本

1.基本的去抖function

 func debounce(interval: Int, queue: DispatchQueue, action: @escaping (() -> Void)) -> () -> Void { var lastFireTime = DispatchTime.now() let dispatchDelay = DispatchTimeInterval.milliseconds(interval) return { lastFireTime = DispatchTime.now() let dispatchTime: DispatchTime = DispatchTime.now() + dispatchDelay queue.asyncAfter(deadline: dispatchTime) { let when: DispatchTime = lastFireTime + dispatchDelay let now = DispatchTime.now() if now.rawValue >= when.rawValue { action() } } } } 

2.参数化去抖function

有时候去抖动function有一个参数是有用的。

 typealias Debounce<T> = (_ : T) -> Void func debounce<T>(interval: Int, queue: DispatchQueue, action: @escaping Debounce<T>) -> Debounce<T> { var lastFireTime = DispatchTime.now() let dispatchDelay = DispatchTimeInterval.milliseconds(interval) return { param in lastFireTime = DispatchTime.now() let dispatchTime: DispatchTime = DispatchTime.now() + dispatchDelay queue.asyncAfter(deadline: dispatchTime) { let when: DispatchTime = lastFireTime + dispatchDelay let now = DispatchTime.now() if now.rawValue >= when.rawValue { action(param) } } } } 

3.例子

在下面的例子中,你可以看到反弹如何工作,使用string参数来标识调用。

 let debouncedFunction = debounce(interval: 200, queue: DispatchQueue.main, action: { (identifier: String) in print("called: \(identifier)") }) DispatchQueue.global(qos: .background).async { debouncedFunction("1") usleep(100 * 1000) debouncedFunction("2") usleep(100 * 1000) debouncedFunction("3") usleep(100 * 1000) debouncedFunction("4") usleep(300 * 1000) // waiting a bit longer than the interval debouncedFunction("5") usleep(100 * 1000) debouncedFunction("6") usleep(100 * 1000) debouncedFunction("7") usleep(300 * 1000) // waiting a bit longer than the interval debouncedFunction("8") usleep(100 * 1000) debouncedFunction("9") usleep(100 * 1000) debouncedFunction("10") usleep(100 * 1000) debouncedFunction("11") usleep(100 * 1000) debouncedFunction("12") } 

注意: usleep()函数仅用于演示目的,可能不是真正的应用程序最优雅的解决scheme。

结果

从最后一次通话起,至less有200毫秒的时间间隔,您总是得到回拨。

叫:4
叫:7
叫:12

以下是为我工作:

将下面的代码添加到项目中的某个文件中(我为此保留了一个“SwiftExtensions.swift”文件):

 // Encapsulate a callback in a way that we can use it with NSTimer. class Callback { let handler:()->() init(_ handler:()->()) { self.handler = handler } @objc func go() { handler() } } // Return a function which debounces a callback, // to be called at most once within `delay` seconds. // If called again within that time, cancels the original call and reschedules. func debounce(delay:NSTimeInterval, action:()->()) -> ()->() { let callback = Callback(action) var timer: NSTimer? return { // if calling again, invalidate the last timer if let timer = timer { timer.invalidate() } timer = NSTimer(timeInterval: delay, target: callback, selector: "go", userInfo: nil, repeats: false) NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSDefaultRunLoopMode) } } 

然后在你的课上设置:

 class SomeClass { ... // set up the debounced save method private var lazy debouncedSave: () -> () = debounce(1, self.save) private func save() { // ... actual save code here ... } ... func doSomething() { ... debouncedSave() } } 

您现在可以重复调用someClass.doSomething() ,它只会每秒保存一次。

使用类的另一个debounce实现,你可能会发现有用的: https : //github.com/webadnan/swift-debouncer

owenoak的解决scheme为我工作。 我改变了一点,以适应我的项目:

我创build了一个Swift文件Dispatcher.swift

 import Cocoa // Encapsulate an action so that we can use it with NSTimer. class Handler { let action: ()->() init(_ action: ()->()) { self.action = action } @objc func handle() { action() } } // Creates and returns a new debounced version of the passed function // which will postpone its execution until after delay seconds have elapsed // since the last time it was invoked. func debounce(delay: NSTimeInterval, action: ()->()) -> ()->() { let handler = Handler(action) var timer: NSTimer? return { if let timer = timer { timer.invalidate() // if calling again, invalidate the last timer } timer = NSTimer(timeInterval: delay, target: handler, selector: "handle", userInfo: nil, repeats: false) NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSDefaultRunLoopMode) NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSEventTrackingRunLoopMode) } } 

然后我在我的UI类中添加了以下内容:

 class func changed() { print("changed") } let debouncedChanged = debounce(0.5, action: MainWindowController.changed) 

与owenoak的anwer关键区别是这条线:

 NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSEventTrackingRunLoopMode) 

没有这一行,如果UI失去焦点,定时器永远不会触发。

这是Swift 3的一个debounce实现。

https://gist.github.com/bradfol/541c010a6540404eca0f4a5da009c761

 import Foundation class Debouncer { // Callback to be debounced // Perform the work you would like to be debounced in this callback. var callback: (() -> Void)? private let interval: TimeInterval // Time interval of the debounce window init(interval: TimeInterval) { self.interval = interval } private var timer: Timer? // Indicate that the callback should be called. Begins the debounce window. func call() { // Invalidate existing timer if there is one timer?.invalidate() // Begin a new timer from now timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(handleTimer), userInfo: nil, repeats: false) } @objc private func handleTimer(_ timer: Timer) { if callback == nil { NSLog("Debouncer timer fired, but callback was nil") } else { NSLog("Debouncer timer fired") } callback?() callback = nil } } 
Interesting Posts