cancelPreviousPerformRequests似乎不适用于Swift 3.0

时间:2017-05-15 15:19:49

标签: ios swift3

正如标题所述,由于某种原因,以下(简化)代码无效:

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时,我都会调用UITextFieldlocalPlaces会调用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

2 个答案:

答案 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件事aTargetaSelector 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的请求   最初是作为论点传递的。