【问题标题】:How to pass callback functions in Swift如何在 Swift 中传递回调函数
【发布时间】:2016-02-03 22:04:53
【问题描述】:

我有一个简单的类,init 方法接受一个 Int 和一个回调函数。

class Timer {
  var timer = NSTimer()
  var handler: (Int) -> Void

  init(duration: Int, handler: (Int) -> Void) {
      self.duration = duration
      self.handler = handler
      self.start()
  }
  @objc func someMethod() {        
      self.handler(10)
  }
}

然后在 ViewController 我有这个:

var timer = Timer(duration: 5, handler: displayTimeRemaining)
func displayTimeRemaining(counter: Int) -> Void {
    println(counter)
}

这不起作用,我得到以下信息:

'Int' is not a subtype of 'SecondViewController'

编辑 1:添加完整代码。

Timer.swift

import UIKit

class Timer {
    lazy var timer = NSTimer()
    var handler: (Int) -> Void

    let duration: Int
    var elapsedTime: Int = 0

    init(duration: Int, handler: (Int) -> Void) {
        self.duration = duration
        self.handler = handler
        self.start()
    }

    func start() {
        self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0,
            target: self,
            selector: Selector("tick"),
            userInfo: nil,
            repeats: true)
    }

    func stop() {
        timer.invalidate()
    }

    func tick() {
        self.elapsedTime++

        self.handler(10)

        if self.elapsedTime == self.duration {
            self.stop()
        }
    }

    deinit {
        self.timer.invalidate()
    }
}

SecondViewController.swift

import UIKit

class SecondViewController: UIViewController {

    @IBOutlet var cycleCounter: UILabel!
    var number = 0

    var timer = Timer(duration: 5, handler: displayTimeRemaining)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func btnIncrementCycle_Click(sender: UIButton){
        cycleCounter.text = String(++number)
        println(number)
    }

    func displayTimeRemaining(counter: Int) -> Void {
        println(counter)
    }
}

我刚开始使用 Swift,所以我很年轻。你应该如何传递回调?我已经查看了示例,我认为这应该可以工作。我的类是否针对我传递回调的方式定义不正确?

谢谢

【问题讨论】:

  • 尝试传递self.displayTimeRemaining。 Swift 可能需要显式引用实例方法来消除歧义;我知道这在其他语言中很常见。 (遗憾的是,我手边没有 mac,所以无法测试这是否为您解决了问题。)
  • 没用。 :( 这是现在的错误:'SecondViewController -> () -> SecondViewController!' does not have a member named 'displayTimeRemaining'。我想知道是不是我在控制器中错误地声明了函数?我使用了您在上面代码中看到的基本语法。
  • init()中的self.duration和self.start()应该是timer.duration和timer.start()吗?
  • @davecom 不是。 startdurationTimer 类的函数和属性,而不是 NSTimertimer 实例。我没有在此处的 sn-p 中包含这些,但它们在我的代码中。无论如何,我认为这不会影响我无法将函数传递给 init 类而不会出错。
  • 你会粘贴完整的代码来实例化 Timer 类变量吗?

标签: swift


【解决方案1】:

好的,现在有了完整的代码,我能够复制您的问题。我不是 100% 确定原因是什么,但我相信这与在类实例化之前引用类方法 (displayTimeRemaining) 有关。以下是解决此问题的几种方法:

选项 1:在 SecondViewController 类之外声明处理程序方法:

func displayTimeRemaining(counter: Int) -> Void {
  println(counter)
}


class SecondViewController: UIViewController {    

  // ...
  var timer = Timer(duration: 5, handler: displayTimeRemaining)

选项 2:通过在函数声明中添加 class 关键字,将 displayTimeRemaining 变成一个类型方法。

class SecondViewController: UIViewController {

  var timer: Timer = Timer(duration: 5, handler: SecondViewController.displayTimeRemaining)

  class func displayTimeRemaining(counter: Int) -> Void {
    println(counter)
  }

选项 3:我相信这将是最符合 Swift 思维方式的 - 使用闭包:

class SecondViewController: UIViewController {

var timer: Timer = Timer(duration: 5) {
        println($0) //using Swift's anonymous method params
    }

【讨论】:

  • 优秀的答案!选项 2 是我使用的选项,效果很好。选项 3 看起来更干净,但每次使用 XCode Beta 7 时都会出现编译错误。不知道有什么问题。也就是说,这个选项更适合简单的回调,如果涉及更多的东西,我认为选项 2 更好。无论如何,感谢您的帮助! :D
  • 是的,很好的答案,我发现选择 2 最有帮助。对于 opt 3,闭包不需要在函数调用中,即Timer(duration: 5, handler: {println($0)} )
  • 两者都可以。我相信当闭包是函数的最后一个参数时,swift 可以让你删除参数并使用我上面显示的语法。
【解决方案2】:

最简单的方法

override func viewDidLoad() {
        super.viewDidLoad()

        testFunc(index: 2, callback: { str in
            print(str)
        })

    }


    func testFunc(index index: Int, callback: (String) -> Void) {
        callback("The number is \(index)")
    }

【讨论】:

    【解决方案3】:

    你的问题是:

    var timer = NSTimer()
    

    您不能以这种方式实例化 NSTimer。它具有为您生成对象的类方法。在这种情况下,解决问题的最简单方法是使定义变得惰性,如下所示:

    lazy var timer = NSTimer()
    

    通过这种方式,计时器的值实际上不会被触及,直到正确设置它的启动方法。注意:可能有更安全的方法来做到这一点。

    【讨论】:

    • 我试过了,但没有任何区别。也许这解决了另一个问题。目前主要的问题是当我在 ViewController 中启动 Time() 时,它不会将我的自定义函数作为处理程序参数。
    猜你喜欢
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    • 2013-06-06
    • 2021-02-08
    • 1970-01-01
    • 1970-01-01
    • 2022-11-25
    • 1970-01-01
    相关资源
    最近更新 更多