正如标题所述,由于某种原因,以下(简化)代码无效:
extension InputView: {
func updateTable(text: String) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(loadPlaces(text:)), object: nil)
//NSObject.cancelPreviousPerformRequests(withTarget: self)
self.perform(#selector(loadPlaces(text:)), with: text, afterDelay: 0.5)
prevSearch = inputField.text!;
}
//Private wrapper function
@objc private func loadPlaces(text: String) {
print("loading results for: \(text)")
// locator?.searchTextHasChanged(text: text)
}
}
每次用户编辑updateTable
时,我都会调用UITextField
,localPlaces
会调用cancelPreviousPerformRequests
,该ViewController
调用查询谷歌在线地点API(注释掉)的功能。不幸的是,每次调用updateTable后都会调用loadPlaces中的打印行。从我的视觉检查来看,实际上打印报表实际上有延迟,但旧的通话不会取消。我已经尝试过查看很多StackOverflow线程,但是我找不到任何针对Swift 3更新的内容。我是否正在调用错误的内容?
PS。如果我改为使用注释掉的单参数class InputView: UIView {
func updateTable(text: String) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(loadPlaces(text:)), object: nil)
self.perform(#selector(loadPlaces(text:)), with: text, afterDelay: 0.5)
}
//Private wrapper function
@objc private func loadPlaces(text: String) {
print("loading results for: \(text)")
// locator?.searchTextHasChanged(text: text)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let input = InputView()
for i in 0..<200 {
input.updateTable(text: "Call \(i)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
。它起作用了。
编辑:我已经能够在单独的项目中复制此错误。因此,我100%确定上述代码是错误的。如果您想复制此错误,请打开一个新的iOS项目并将以下代码粘贴到默认的127.0.0.1
中:
localhost
答案 0 :(得分:5)
Duncan C的答案中的解释不适合这种情况。
在cancelPreviousPerformRequests(withTarget:selector:object:)
的参考文献中:
讨论
取消所有执行请求,其目标与
aTarget
相同,参数为anArgument
,选择器为aSelector
。
所以,当你有一行像:
<aTarget>.perform(<aSelector>, with: <anArgument>, afterDelay: someDelay)
您可以通过以下方式取消:
NSObject.cancelPreviousPerformRequests(withTarget: <aTarget>, selector: <aSelector>, object: <anArgument>)
仅当所有3件事aTarget
,aSelector
和anArgument
匹配时。
请尝试这样的事情并查看你看到的内容:
class InputView: UIView {
var lastPerformArgument: NSString? = nil
func updateTable(text: String) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(loadPlaces(text:)), object: lastPerformArgument)
lastPerformArgument = text as NSString
self.perform(#selector(loadPlaces(text:)), with: lastPerformArgument, afterDelay: 0.5)
}
//Private wrapper function
@objc private func loadPlaces(text: String) {
print("loading results for: \(text)")
// locator?.searchTextHasChanged(text: text)
}
}
答案 1 :(得分:1)
这个答案的第一部分是错误的。有关更新信息,请参阅底部的编辑。我将离开原来的答案,因为讨论可能有用。
在我看来,NSObject
将Swift函数名映射到选择器的方式存在错误,导致其无法正常工作。我能够让cancelPreviousPerformRequests
函数实际取消挂起的perform()
的唯一方法是函数没有任何参数。如果函数采用单个匿名参数或命名参数,则cancelPreviousPerformRequests
函数不会取消挂起的perform(_:with:afterDelay:)
。
我发现的另一个错误:如果你使用带有匿名参数的函数,例如:
func foo(_ value: String) {
print("In function \(#function)")
}
然后你在print语句中看到的结果是:
In function foo
如果函数有2个,3个或更多个匿名参数,你会看到同样的事情。
如果你有一个没有参数的函数,你会得到不同的结果:
func foo() {
print("In function \(#function)")
}
该代码将显示消息:
In function foo()
(注意函数名后面的括号。)
请注意,我似乎错了。显然,object
的{{1}}参数必须与传入的内容相匹配。如果使用nil参数调用选择器,则只能将cancelPreviousPerformRequests
传递给object:nil
。
引用文档:
以前在注册时使用的请求的参数 执行(:with:afterDelay :)实例方法。争论平等是 使用isEqual( :)确定,因此该值不必是同一个对象 最初通过的。传递nil以匹配nil的请求 最初是作为论点传递的。