macro_rules! msg_send_id {
    [$obj:expr, $selector:ident $(,)?] => { ... };
    [$obj:expr, $($selector:ident : $argument:expr),+ $(,)?] => { ... };
}
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 easily tell 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 (here T: Message and O: Ownership):

  • The new family: The receiver must be &Class, and the return type is a generic Id<T, O> or Option<Id<T, O>>.

  • The alloc family: The receiver must be &Class, and the return type is a generic Id<Allocated<T>, O> or Option<Id<Allocated<T>, O>>.

  • The init family: The receiver must be Option<Id<Allocated<T>, O>> as returned from alloc. The receiver is consumed, and a the now-initialized Id<T, O> or Option<Id<T, O>> (with the same T and O) is returned.

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

  • 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, O> or Option<Id<T, O>> 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 always 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<_, _> this macro will automatically unwrap the object, or panic with an error message if it couldn’t be retrieved.

This macro doesn’t support super methods yet, see #173. The retain, release and autorelease selectors are not supported, use Id::retain, Id::drop and Id::autorelease for that.

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