Reputation: 7417
Question Summary:
If you have a Swift class that takes a selector as an argument in its initializer, how do you manually "fire/call" that selector?
Full Question:
Consider the following attempt at making a custom timer in 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")
}
}
}
The usage of this "ValueAnimator" would be similar to a normal Timer/NSTimer, in that you pass a "selector" as an argument and that selector is called each time the ValueAnimator fires:
[In Parent Class]:
// { ...
let valueAnimatorTest = ValueAnimator(durationInSeconds: 10, selector: #selector(self.temp))
valueAnimatorTest.start()
}
@objc func temp() {
print("temp VA callback works!") // this doesn't happen :(
}
I'm trying to implement the same thing and as I understand, the line:
perform(selector)
should fire the selector in the parent class, but instead I get the error: "unrecognized selector sent to instance 0x600001740030"
I'm in a bit over my head here. I have tried googling the error, but everyone seems to be talking about how to use a selector from the parent-side (how to use Timer.scheduledTimer(), etc.) but I already know how to do that successfully.
I've also tried various tweaks to the code (changing public/private, scope of variables, and different forms of the performSelector() function)... but can't figure out the proper way to make the selector fire... or the unrelated mistake I've made if there is one.
Thanks for any help.
Upvotes: 3
Views: 5674
Reputation: 307
By calling perform(selector)
it's like you're calling self.perform(selector)
(self is implied), and by doing so the current instance of the ValueAnimator class is the object that actually performs the selector. When that happens, it tries to call a method called temp()
of the ValueAnimator class, but as it doesn't exist the app is crashing.
You can verify that if you add a temp()
method in the ValueAnimator:
@objc func temp() {
print("Wrong method!!!")
}
If you run now you'll have no crash and the "Wrong Selector!!!" message will appear on the console.
The solution to your problem is to pass the object that should run the selector method along with the selector to the initialisation of the ValueAnimator object.
In the ValueAnimator class declare the following property:
private var target: AnyObject
Update the init
method so it can get the target as an argument:
init(durationInSeconds: Int, selector: Selector, target: AnyObject) {
...
self.target = target
}
Also update the timerCallback()
:
@objc func timerCallback() {
...
_ = target.perform(selector)
...
}
Finally, when you initialise a ValueAnimator instance pass the object that the selector belongs to as well:
let valueAnimatorTest = ValueAnimator(durationInSeconds: 10, selector: #selector(self.temp), target: self)
Run again and the proper temp()
method will be executed this time.
I hope it helps.
Upvotes: 6
Reputation: 16327
You are calling perform on the wrong object: its an instance method of NSObject
, so you are trying to call perform
on ValueAnimator
and ValueAnimator
does not respond to "temp"
. You must pass in both the object and the selector you want to perform, then you call perform on that object with the selector. Notice that this is exactly what Timer
does: you have to pass in self as the object and the timer call the selector you specify on self.
Upvotes: 1