Hannibal #selector

The selector is key to Objective-C’s dynamic runtime nature. It’s
just a name that’s used, at runtime, as a key into a dictionary of function
pointers. Whenever you send a message to an Objective-C object,
you’re actually using a selector to look up a function to
call. Sometimes selectors bubble up in Cocoa/CocoaTouch API, and you
will need to deal with them in Swift.

In Cocoa/CocoaTouch, selectors are a way of telling an object like a
UIButton “When someone taps you, I want you to send this message to
this particular object.” The name of the method to invoke,
and the object to send the message to, are potentially not
known until runtime. Most of the time these connections are made in a
xib file or a storyboard, but you can do it in code too:

Tapping the button causes self to be sent the message
showAboutBox:. If there is no showAboutBox: method, you’ll die
at runtime with an undefined selector exception.

@selector(message:name:argument:names:too:) is an Objective-C
compiler feature that turns a sequence of characters that happens to
look like an Objective-C method name into some key useful for looking
up code at runtime. There is no type information encoded in the
selector, outside of the number of colons indicating the number of
arguments expected by the method.

This kind of API is common with Cocoa/CocoaTouch’s “target/action”
architecture, used in most of the UI controls, as well as with
NSNotificationCenter. This is a fairly old design—APIs
that take blocks/closures are more common these days.

Why this kind of convoluted machinery of storing a name and then later
looking it up at runtime? It lets you use very descriptive names for
handler functions. You can easily figure out what openAboutBox:
would do. If you had to subclass a Button class and override
tapped() all the time, it would be harder to determine at a glance
what the button handler does. It also allows tooling, such as
Interface Builder, to set up these connections without having to
generate code. It just saves the selector name in a resource file.

Selectors in Swift

You use Cocoa/CocoaTouch from Swift, and so you want button taps to
invoke Swift code. Swift needs to be able to use selectors. Selectors
aren’t part of the default Swift runtime behavior, because sending
arbitrary messages to objects can be unsafe. The compiler can’t guarantee
that the receiving object actually responds to that selector.

To have your Swift objects participate in the Objective-C runtime, you
will need to opt-in by having your class inherit from NSObject, or
decorate individual methods with @objc. This makes a Swift class
participate in Objective-C’s method dispatch mechanism. If you’re
curious about how Objective-C’s runtime does its thing, check out the
Inside the
Bracket
extravaganza.

Here’s the equivalent code in Swift for adding a new callback to a UIButton:

Why not just use a string, like “showAboutBox,” similar to how
@selector works? Method names get automatically renamed going
between Objective-C and Swift—sometimes an argument name goes before
the opening paren, sometimes after. Maybe there’s an NSError**
involved. Remembering all the rules is tedious and error-prone. If
you spend all your time in Swift, you don’t need to juggle a bunch of
Objective-C details in your head. Sounds like a great job for a
compiler, though.

When Swift sees #selector it examines the
method being referenced, and then derives a selector from it. Any
necessary name rewriting is done automatically. This is why you usually
supply the class name to #selector—the compiler can unambiguously
determine the information it needs about this particular method, such as, “Does
this thing actually exist?” or “Has the selector name been explictly renamed?”

If the method you’re getting a #selector for is defined in the same class where
you’re referencing it (say in a view controller’s viewDidAppear referencing
methods in that same view controller), you can leave off the explicit class
name. Be aware that Xcode won’t properly autocomplete it for you, instead
only offering a function call-site rather than just a reference to the function.

For the rest of this post, I’ll be using the fully-qualified form.

Selector Syntax

Objective-C’s selector syntax is pretty simple - it just uses
@selector(methodName:arguments:). Swift’s is a bit more complicated.

In its simplest form, you need to provide a class name that defines
(or inherits) the message, and then the method name without any extra
decoration:

When you call doStuff directly, Swift can figure out which one of
these to use based on the arguments you pass. Indirect calling by
selector doesn’t have the luxury of knowing the argument’s types. If
you try to make a selector for doStuff, you will get an error about
ambiguous use of doStuff:

The Rename Game

Swift classes that participate in the Objective-C runtime have
selectors automatically built by the compiler. The Swift compiler
bases the selectors on the method’s name. Sometimes you may want to
expose a different name to Objective-C, a
name that’s more in line with what an Objective-C developer is
expecting.

You might also have overloaded functions that don’t differ by number
of arguments (just types). The selector then becomes ambiguous.
Here’s a pure-Swift class:

This works fine in your app. Then you decide to inherit from NSObject
so a Blorf object can receive UIButton taps:

Objective-C demands that all methods have unique selectors. Objective-C selectors don’t carry type information, so the selectors
for both takesAnArgument methods will be the same: @selector(takesAnArgument:). You either have
to rename one of them, or tell the compiler to keep the Swift name and
use a different name for the Objective-C selector:

(Blorf.takesAnArgument(_:)) - This is the Swift method name name
that you want a selector for.

(... as (Blorf) -> - this is the curried self, also known as the
instance self. Just think of this as the type of a hidden
parameter which ends up being self inside of the method.

... (Double) -> Void) - this is the signature of the method
without the self parameter. In this case, this selector
references the version of takesAnArgument that takes a Double and
returns nothing. This is the one that was decorated with
@objc(takesADoubleArgument:), so the selector ultimately generated
by this expression would be an Objective-C
@selector(takesADoubleArgument:)

Swift 3 Additions

Swift 3 extends the #selector syntax with getter: and setter: arguments. When determining a selector for an Objective-C propery, you need to specify
whether you want the setter or getter. Use it like this:

letsel=#selector(getter: UIViewController.preferredContentSize)

Older Syntax

#selector was introduced in Swift 2.2. Prior versions of Swift used a
String mechanism to automatically convert selector-looking strings
into selectors. If you use this syntax today, you will get a
deprecation warning.

You can also use Selector("someSelectorName") as an alternative syntax,
but the compiler will give you a warning suggesting you use #selector,
assuming it has seen this selector anywhere before. You can use this syntax to
generate a selector that the compiler hasn’t seen yet, perhaps as a
selector to methods that are dynamically loaded by plugins at
runtime.

Danger!

Remember that selectors in Objective-C don’t carry along type
information. The #selector syntax is just used to derive the proper
Objective-C selector to reference a particular Swift method. There is
no safety checking performed. This means you’re welcome to create a
Swift method that takes 17 generic enum parameters and use it for a
UIButton action. You’re also welcome to crash hard at runtime. Swift doesn’t make using selector-based API foolproof.

TL;DR

Here’s the syntax, as of Swift 2.2, for selectors. Unindented lines
indicate what particular kinds of methods are being #selectored, and
indented lines are using actual types. The first form includes the class name.

If you’re getting a selector for something in the current class (or a superclass), you can use the second, more compact form. Be aware that Xcode (at least as of 7.3) won’t properly autocomplete that form.