Macro objc2::sel

source ·
macro_rules! sel {
    (new) => { ... };
    (init) => { ... };
    (alloc) => { ... };
    (dealloc) => { ... };
    ($sel:ident) => { ... };
    ($($sel:ident :)*) => { ... };
    ($($sel:tt)*) => { ... };
}
Expand description

Register a selector with the Objective-C runtime.

Returns the Sel corresponding to the specified selector.

§Panics

Panics if the runtime failed allocating space for the selector.

§Specification

This has similar syntax and functionality as the @selector directive in Objective-C.

This calls Sel::register internally. The result is cached for efficiency. The cache for certain common selectors (alloc, init and new) is deduplicated to reduce code-size.

Non-ascii identifiers are ill-tested, if supported at all.

§Features

If the experimental "unstable-static-sel" feature is enabled, this will emit special statics that will be replaced by the dynamic linker (dyld) when the program starts up - in exactly the same manner as normal Objective-C code does. This should be significantly faster (and allow better native debugging), however due to the Rust compilation model, and since we don’t have low-level control over it, it is currently unlikely that this will work correctly in all cases. See the source code and rust-lang/rust#53929 for more info.

Concretely, this may fail at:

  • link-time (likely)
  • dynamic link-time/just before the program is run (fairly likely)
  • runtime, causing UB (unlikely)

The "unstable-static-sel-inlined" feature is the even more extreme version - it yields the best performance and is closest to real Objective-C code, but probably won’t work unless your code and its inlining is written in a very certain way.

Enabling LTO greatly increases the chance that these features work.

§Examples

Get a few different selectors:

use objc2::sel;
let sel = sel!(alloc);
let sel = sel!(description);
let sel = sel!(_privateMethod);
let sel = sel!(storyboardWithName:bundle:);
let sel = sel!(
    otherEventWithType:
    location:
    modifierFlags:
    timestamp:
    windowNumber:
    context:
    subtype:
    data1:
    data2:
);

Whitespace is ignored:

let sel1 = sel!(setObject:forKey:);
let sel2 = sel!(  setObject  :

    forKey  : );
assert_eq!(sel1, sel2);

Invalid selector:

let sel = sel!(aSelector:withoutTrailingColon);

A selector with internal colons:

let sel = sel!(sel::with:::multiple:internal::::colons:::);

// Yes, that is possible! The following Objective-C would work:
//
// @interface MyThing: NSObject
// + (void)test:(int)a :(int)b arg:(int)c :(int)d;
// @end

Unsupported usage that you may run into when using macros - fails to compile when the "unstable-static-sel" feature is enabled.

Instead, define a wrapper function that retrieves the selector.

use objc2::sel;
macro_rules! x {
    ($x:ident) => {
        // One of these is fine
        sel!($x);
        // But using the identifier again in the same way is not!
        sel!($x);
    };
}
// Identifier `abc`
x!(abc);