【问题标题】:How to properly fire/call a "selector" in Swift?如何在 Swift 中正确触发/调用“选择器”?
【发布时间】:2019-11-20 01:32:48
【问题描述】:

问题总结:

如果您有一个 Swift 类,它在其初始化程序中将选择器作为参数,您如何手动“触发/调用”该选择器?

完整问题:

考虑以下在 Swift 中创建自定义计时器的尝试:

let TIME_INTERVAL = 0.1

class ValueAnimator : NSObject {
    private var timer = Timer()

    private let maxRep: Int
    private var currentRepIndex: Int = 0

    private var selector: Selector

    init(durationInSeconds: Int, selector: Selector) {
        print("VALUEANIMATOR INIT")
        self.maxRep = Int(Double(durationInSeconds) / TIME_INTERVAL)
        self.selector = selector

    }

    func start() {
        timer = Timer.scheduledTimer(timeInterval: TIME_INTERVAL, target: self, selector: (#selector(timerCallback)), userInfo: nil, repeats: true)
    }

    @objc func timerCallback() {
        currentRepIndex += 1

        perform(selector) // <-------- this line causes crash, "unrecognized selector sent to instance 0x600001740030"

        print ("VA timer called!, rep: \(currentRepIndex)")
        if currentRepIndex == maxRep {
            timer.invalidate()
            print("VA timer invalidated")
        }

    }


}

这个“ValueAnimator”的用法与普通的 Timer/NSTimer 类似,因为您将“选择器”作为参数传递,并且每次 ValueAnimator 触发时都会调用该选择器:

[在父类中]:

// { ...

   let valueAnimatorTest = ValueAnimator(durationInSeconds: 10, selector: #selector(self.temp))
        valueAnimatorTest.start()
    }

    @objc func temp() {
        print("temp VA callback works!") // this doesn't happen :(
    }

我正在尝试实现同样的事情,据我所知,这行:

perform(selector)

应该在父类中触发选择器,但我收到错误:“无法识别的选择器发送到实例 0x600001740030”

我在这里有点过头了。我试过用谷歌搜索错误,但似乎每个人都在谈论如何使用父方的选择器(如何使用 Timer.scheduledTimer() 等),但我已经知道如何成功地做到这一点。

我还尝试了对代码的各种调整(更改公共/私有、变量范围和 performSelector() 函数的不同形式)...但无法找出使选择器触发的正确方法...或者我犯的不相关的错误,如果有的话。

感谢您的帮助。

【问题讨论】:

  • ValueAnimator 本身是否超出范围并被删除?

标签: ios swift timer callback selector


【解决方案1】:

调用perform(selector) 就像调用self.perform(selector) 一样(隐含self),通过这样做,ValueAnimator 类的当前实例就是实际执行选择器的对象。发生这种情况时,它会尝试调用 ValueAnimator 类的名为 temp() 的方法,但由于它不存在,因此应用程序正在崩溃。

如果在 ValueAnimator 中添加temp() 方法,可以验证:

@objc func temp() {
    print("Wrong method!!!")
}

如果您现在运行,您将不会出现崩溃和“错误的选择器!!!”消息将出现在控制台上。

解决您的问题的方法是将应该运行选择器方法的对象与选择器一起传递给 ValueAnimator 对象的初始化。

在 ValueAnimator 类中声明以下属性:

private var target: AnyObject

更新init 方法,使其可以获取目标作为参数:

init(durationInSeconds: Int, selector: Selector, target: AnyObject) {
    ...

    self.target = target
}

同时更新timerCallback():

@objc func timerCallback() {
    ...

    _ = target.perform(selector)

    ...
}

最后,当你初始化一个 ValueAnimator 实例时,也要传递选择器所属的对象:

let valueAnimatorTest = ValueAnimator(durationInSeconds: 10, selector: #selector(self.temp), target: self)

再次运行,这次将执行正确的temp()方法。

希望对你有帮助。

【讨论】:

  • 感谢您提供如此详尽的答案!
【解决方案2】:

您在错误的对象上调用 perform:它是 NSObject 的实例方法,因此您尝试在 ValueAnimator 上调用 perform 并且 ValueAnimator 不响应 "temp"。您必须同时传入要执行的对象和选择器,然后使用选择器对该对象调用 perform。请注意,这正是 Timer 所做的:您必须将 self 作为对象传入,并且计时器会调用您在 self 上指定的选择器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 2022-07-18
    相关资源
    最近更新 更多