@selector
is keyword in Objective-C. It could convert a method to a SEL type, which turns out behaving as a "function pointer". In the old Objective-C's age, selector is widely used, from setting the target-action to introspecting. In Objective-C, the way of generating a selector would be:
-(void) callMe {
//...
}
-(void) callMeWithParam:(id)obj {
//...
}
SEL someMethod = @selector(callMe);
SEL anotherMethod = @selector(callMeWithParam:);
// Or we can use NSSelectorFromString as well
// SEL someMethod = NSSelectorFromString(@"callMe");
// SEL anotherMethod = NSSelectorFromString(@"callMeWithParam:");
For writing less code, @selector
is widely used. But if we need to decide which method to call at runtime, NSSelectorFromString
would be preferred, so we could generate the method string dynamically and send the message with the name.
There is no @selector
keyword anymore in Swift. Instead of it, from Swift 2.2, we could get a selector by using #selector
and pass the function name which exposed to Objective-C to it. Similarly, the original SEL
type is also replaced by a Selector
struct. The two examples above could be rewritten as below:
func callMe() {
//...
}
func callMeWithParam(obj: AnyObject!) {
//...
}
let someMethod = #selector(callMe)
let anotherMethod = #selector(callMeWithParam(_:))
Same as Objective-C, you have to add the colon (:
) following callMeWithParam
to construct the full method name. Method with multiple parameters is similar, like this:
func turnByAngle(theAngle: Int, speed: Float) {
//...
}
let method = #selector(turnByAngle(_:speed:))
Besides of that, since Selector
type conforms the StringLiteralConvertible
protocol, we could even use a string to assign a selector, without using its init method explicitly. It is semantic in some situation, for example, setting a call back method when adding a notification observer:
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "callMe", name: "CallMeNotification", object: nil)
We should pay special attention on the fact of selector being a concept of Objective-C runtime. If the method of a selector is only exposed in Swift, but not Objective-C (or in other words, it is a private method of Swift), you might get an "unrecognized selector" exception when sending method to this selector:
This is wrong code
private func callMe() { //... }
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector:#selector(callMe), userInfo: nil, repeats: true) ```
One solution would be adding @objc
keyword before private
, so the runtime could get to know what you mean by the selector.
@objc private func callMe() {
//...
}
NSTimer.scheduledTimerWithTimeInterval(1, target: self,
selector:#selector(callMe), userInfo: nil, repeats: true)
Lastly, if the method name is unique in the scope it belongs to, we could use the name alone without full signature/ Compared to the full signature, it will be a bit easier for us to write the short version:
let someMethod = #selector(callMe)
let anotherMethod = #selector(callMeWithParam)
let method = #selector(turnByAngle)
However, if there are two or more functions with the same name in the same scope, Swift will not be happy, even the function signatures are different between them:
func commonFunc() {
}
func commonFunc(input: Int) -> Int {
return input
}
let method = #selector(commonFunc)
// Compile error, there is ambiguity in `commonFunc`
We have to cast them to corresponded types to make it compile:
let method1 = #selector(commonFunc as ()->())
let method2 = #selector(commonFunc as Int->Int)