Macro objc2::msg_send_id

source ·
macro_rules! msg_send_id {
    [super($obj:expr), $($selector_and_arguments:tt)+] => { ... };
    [super($obj:expr, $superclass:expr), $($selector_and_arguments:tt)+] => { ... };
    [$obj:expr, new $(,)?] => { ... };
    [$obj:expr, alloc $(,)?] => { ... };
    [$obj:expr, init $(,)?] => { ... };
    [$obj:expr, $($selector_and_arguments:tt)+] => { ... };
}
Expand description

msg_send! for methods returning id, NSObject*, or similar object pointers.

Object pointers in Objective-C have certain rules for when they should be retained and released across function calls. This macro helps doing that, and returns an rc::Id with the object, optionally wrapped in an Option if you want to handle failures yourself.

A little history

Objective-C’s type system is… limited, so you can’t tell without consulting the documentation who is responsible for releasing an object. To remedy this problem, Apple/Cocoa introduced (approximately) the following rule:

The caller is responsible for releasing objects return from methods that begin with new, alloc, copy, mutableCopy or init, and method that begins with init takes ownership of the receiver. See Cocoa’s Memory Management Policy for a user-friendly introduction to this concept.

In the past, users had to do retain and release calls themselves to properly follow these rules. To avoid the memory management problems associated with manual stuff like that, they introduced “ARC”, which codifies the rules as part of the language, and inserts the required retain and release calls automatically.

msg_send! is similar to pre-ARC; you have to know when to retain and when to release an object. msg_send_id! is similar to ARC; the rules are simple enough that we can do them automatically!

Specification

The syntax is the same as in msg_send!.

Attributes like objc_method_family, ns_returns_retained, ns_consumed and so on must not present on the method - if they are, you should do manual memory management using the msg_send! macro instead.

The accepted receiver and return types, and how we handle them, differ depending on which, if any, of the recognized selector families the selector belongs to:

  • The new family: The receiver may be anything that implements MessageReceiver (though often you’ll want to use &AnyClass). The return type is a generic Id<T> or Option<Id<T>>.

  • The alloc family: The receiver must be &AnyClass, and the return type is a generic Allocated<T>.

  • The init family: The receiver must be Allocated<T> as returned from alloc, or if sending messages to the superclass, it must be PartialInit<T>.

    The receiver is consumed, and a the now-initialized Id<T> or Option<Id<T>> (with the same T) is returned.

  • The copy family: The receiver may be anything that implements MessageReceiver and the return type is a generic Id<T> or Option<Id<T>>.

  • The mutableCopy family: Same as the copy family.

  • No family: The receiver may be anything that implements MessageReceiver. The result is retained using Id::retain_autoreleased, and a generic Id<T> or Option<Id<T>> is returned. This retain is in most cases faster than using autorelease pools!

See the clang documentation for the precise specification of Objective-C’s ownership rules.

As you may have noticed, the return type is usually either Id or Option<Id>. Internally, the return type is always Option<Id> (for example: almost all new methods can fail if the allocation failed), but for convenience, if the return type is Id<T>, this macro will automatically unwrap the object, or panic with an error message if it couldn’t be retrieved.

As a special case, if the last argument is the marker _, the macro will return a Result<Id<T>, Id<E>>, see below.

The retain, release and autorelease selectors are not supported, use Id::retain, Id::drop and Id::autorelease for that.

Errors

Very similarly to msg_send!, this macro supports transforming the return type of methods whose last parameter is NSError** into the Rust equivalent, the Result type.

In particular, you can make the last argument the special marker _, and then the macro will return a Result<Id<T>, Id<E>> (where you must specify E yourself, usually you’d use icrate::Foundation::NSError).

Panics

Panics if the return type is specified as Id<_, _> and the method returned NULL.

Additional panicking cases are documented in msg_send!.

Safety

Same as msg_send!, with an expected return type of id, instancetype, NSObject*, or other such object pointers. The method must not have any attributes that changes the how it handles memory management.

Note that if you’re using this inside a context that expects unwinding to have Objective-C semantics (like exception::catch), you should make sure that the return type is Option<Id<_, _>> so that you don’t get an unexpected unwind through incompatible ABIs!

Examples

use objc2::{class, msg_send_id};
use objc2::ffi::NSUInteger;
use objc2::rc::Id;
use objc2::runtime::NSObject;
// Allocate new object
let obj = unsafe { msg_send_id![class!(NSObject), alloc] };
// Consume the allocated object, return initialized object
let obj: Id<NSObject> = unsafe { msg_send_id![obj, init] };
// Copy the object
let copy: Id<NSObject> = unsafe { msg_send_id![&obj, copy] };
// Call ordinary selector that returns an object
// This time, we handle failures ourselves
let s: Option<Id<NSObject>> = unsafe { msg_send_id![&obj, description] };
let s = s.expect("description was NULL");