如何等待给定时间并只执行最后一个函数调用
How to wait for a given time and only perform last function call
假设我有一个名为 Person
的 class,变量如 firstName
和 lastName
。我正在使用 reactiveCocoa 框架监听这些变量的变化,但假设我只使用内置的 KVO 监听,比如 didSet{}
。所以假设我有这个代码:
let firstName:String { didSet{ self.nameDidChange() }}
let lastName: String { didSet{ self.nameDidChange() }}
func nameDidChange(){ print("New name:", firstName, lastName}
每次我更改名字或姓氏时,它都会自动调用函数 nameDidChange
。
我想知道的是,当我同时更改 firstName
和 lastName
.[=33 时,这里是否有任何明智的举动可以防止 nameDidChange
函数被连续调用两次=]
假设 firstName
中的值是 "Anders"
而 lastName
是 "Andersson"
,那么我 运行 这段代码:
firstName = "Borat"
lastName = "Boratsson"
nameDidChange
这里会被调用两次。它会先打印出"New name: Borat Andersson"
,然后是"New name: Borat Boratsson"
.
在我简单的想法中,我想我可以创建一个名为 nameIsChanging()
的函数,每当 didSet
中的任何一个被调用时调用它,并启动一个 0.1 秒的计时器, 并且 then 调用 nameDidChange()
, 但是这两个 didSet
s 也会调用 nameIsChanging
, 所以定时器会走两次, 并且触发两次.为了解决这个问题,我可以保留一个 "global" Timer
,并让它自己失效并重新开始计数或类似的东西,但我越想解决方案,它们就越难看。这里有"best practices"吗?
我认为您的方向是正确的。我认为您只需要延迟对 name changed 的调用,直到用户 "Stopped" 输入。
像这样:
var timer = Timer()
var firstName: String = "Clint" {
didSet {
timer.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: { _ in
self.nameDidChange()
})
}
}
var secondName: String = "Eastwood" {
didSet {
timer.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: { _ in
self.nameDidChange()
})
}
}
func nameDidChange() {
print(firstName + secondName)
}
每次更改名字或第二个名字时,它都会停止计时器并再等待 0.2 秒,直到它提交名称更改。
编辑
看完Adam Venturella的评论后,我意识到这确实是一种去抖动技术。如果您想进一步了解它,google 这个概念会很有用。
这是一个简单的 Playground 来说明这个概念:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
var timer: Timer? = nil
func nameDidChange() {
print("changed")
}
func debounce(seconds: TimeInterval, function: @escaping () -> Swift.Void ) {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: false, block: { _ in
function()
})
}
debounce(seconds: 0.2) { nameDidChange() }
debounce(seconds: 0.2) { nameDidChange() }
debounce(seconds: 0.2) { nameDidChange() }
debounce(seconds: 0.2) { nameDidChange() }
debounce(seconds: 0.2) { nameDidChange() }
debounce(seconds: 0.2) { nameDidChange() }
输出:
changed
nameDidChange 函数只执行了一次。
不确定我是否正确理解了您的问题,但您可以尝试利用 Date
来查看它们是否在彼此之后被解雇,而不是计时器。另请注意,.timeIntervalSince1970
returns 自 1970 年以来的秒数,因此我将其乘以 100 以获得更好的精度。
var firstName:String! { didSet{ self.nameDidChange() }}
var lastName: String! { didSet{ self.nameDidChange() }}
var currentDate: UInt64 = UInt64((Date().timeIntervalSince1970 * 100)) - 100
func nameDidChange(){
let now = UInt64(Date().timeIntervalSince1970 * 100)
//You can add a higher tolerance here if you wish
if (now == currentDate) {
print("firing in succession")
} else {
print("there was a delay between the two calls")
}
currentDate = now
}
编辑:虽然这不是 运行 最后一次通话,而是第一次通话,但也许这可能有助于/激发一些想法
合并对 nameDidChange()
的调用的一种简单方法是通过 DispatchQueue.main.async
调用它,除非这样的调用已经挂起。
class MyObject {
var firstName: String = "" { didSet { self.scheduleNameDidChange() } }
var lastName: String = "" { didSet { self.scheduleNameDidChange() } }
private func scheduleNameDidChange() {
if nameDidChangeIsPending { return }
nameDidChangeIsPending = true
RunLoop.main.perform { self.nameDidChange() }
}
private func nameDidChange() {
nameDidChangeIsPending = false
print("New name:", firstName, lastName)
}
private var nameDidChangeIsPending = false
}
如果单个 UI 事件(例如触摸)导致对 firstName
和 lastName
进行多次更改,则 nameDidChange()
将仅被调用一次。
假设我有一个名为 Person
的 class,变量如 firstName
和 lastName
。我正在使用 reactiveCocoa 框架监听这些变量的变化,但假设我只使用内置的 KVO 监听,比如 didSet{}
。所以假设我有这个代码:
let firstName:String { didSet{ self.nameDidChange() }}
let lastName: String { didSet{ self.nameDidChange() }}
func nameDidChange(){ print("New name:", firstName, lastName}
每次我更改名字或姓氏时,它都会自动调用函数 nameDidChange
。
我想知道的是,当我同时更改 firstName
和 lastName
.[=33 时,这里是否有任何明智的举动可以防止 nameDidChange
函数被连续调用两次=]
假设 firstName
中的值是 "Anders"
而 lastName
是 "Andersson"
,那么我 运行 这段代码:
firstName = "Borat"
lastName = "Boratsson"
nameDidChange
这里会被调用两次。它会先打印出"New name: Borat Andersson"
,然后是"New name: Borat Boratsson"
.
在我简单的想法中,我想我可以创建一个名为 nameIsChanging()
的函数,每当 didSet
中的任何一个被调用时调用它,并启动一个 0.1 秒的计时器, 并且 then 调用 nameDidChange()
, 但是这两个 didSet
s 也会调用 nameIsChanging
, 所以定时器会走两次, 并且触发两次.为了解决这个问题,我可以保留一个 "global" Timer
,并让它自己失效并重新开始计数或类似的东西,但我越想解决方案,它们就越难看。这里有"best practices"吗?
我认为您的方向是正确的。我认为您只需要延迟对 name changed 的调用,直到用户 "Stopped" 输入。
像这样:
var timer = Timer()
var firstName: String = "Clint" {
didSet {
timer.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: { _ in
self.nameDidChange()
})
}
}
var secondName: String = "Eastwood" {
didSet {
timer.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false, block: { _ in
self.nameDidChange()
})
}
}
func nameDidChange() {
print(firstName + secondName)
}
每次更改名字或第二个名字时,它都会停止计时器并再等待 0.2 秒,直到它提交名称更改。
编辑
看完Adam Venturella的评论后,我意识到这确实是一种去抖动技术。如果您想进一步了解它,google 这个概念会很有用。
这是一个简单的 Playground 来说明这个概念:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
var timer: Timer? = nil
func nameDidChange() {
print("changed")
}
func debounce(seconds: TimeInterval, function: @escaping () -> Swift.Void ) {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: false, block: { _ in
function()
})
}
debounce(seconds: 0.2) { nameDidChange() }
debounce(seconds: 0.2) { nameDidChange() }
debounce(seconds: 0.2) { nameDidChange() }
debounce(seconds: 0.2) { nameDidChange() }
debounce(seconds: 0.2) { nameDidChange() }
debounce(seconds: 0.2) { nameDidChange() }
输出:
changed
nameDidChange 函数只执行了一次。
不确定我是否正确理解了您的问题,但您可以尝试利用 Date
来查看它们是否在彼此之后被解雇,而不是计时器。另请注意,.timeIntervalSince1970
returns 自 1970 年以来的秒数,因此我将其乘以 100 以获得更好的精度。
var firstName:String! { didSet{ self.nameDidChange() }}
var lastName: String! { didSet{ self.nameDidChange() }}
var currentDate: UInt64 = UInt64((Date().timeIntervalSince1970 * 100)) - 100
func nameDidChange(){
let now = UInt64(Date().timeIntervalSince1970 * 100)
//You can add a higher tolerance here if you wish
if (now == currentDate) {
print("firing in succession")
} else {
print("there was a delay between the two calls")
}
currentDate = now
}
编辑:虽然这不是 运行 最后一次通话,而是第一次通话,但也许这可能有助于/激发一些想法
合并对 nameDidChange()
的调用的一种简单方法是通过 DispatchQueue.main.async
调用它,除非这样的调用已经挂起。
class MyObject {
var firstName: String = "" { didSet { self.scheduleNameDidChange() } }
var lastName: String = "" { didSet { self.scheduleNameDidChange() } }
private func scheduleNameDidChange() {
if nameDidChangeIsPending { return }
nameDidChangeIsPending = true
RunLoop.main.perform { self.nameDidChange() }
}
private func nameDidChange() {
nameDidChangeIsPending = false
print("New name:", firstName, lastName)
}
private var nameDidChangeIsPending = false
}
如果单个 UI 事件(例如触摸)导致对 firstName
和 lastName
进行多次更改,则 nameDidChange()
将仅被调用一次。