问题摘要:
如果你有一个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((函数(。。。但无法找到正确的方法让选择器启动。。。或者我犯过的不相关的错误(如果有的话(。
谢谢你的帮助。
通过调用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()
方法。
我希望它能有所帮助。
您在错误的对象上调用perform:它是NSObject
的实例方法,因此您试图在ValueAnimator
上调用perform
,而ValueAnimator
没有响应"temp"
。必须传入要执行的对象和选择器,然后使用选择器对该对象调用perform。请注意,这正是Timer
所做的:您必须传入self作为对象,计时器调用您在self上指定的选择器。