Message

Trait Message 

Source
pub unsafe trait Message: RefEncode {
    // Provided method
    fn retain(&self) -> Retained<Self> 
       where Self: Sized { ... }
}
Expand description

Types that can be sent Objective-C messages.

Implementing this provides MessageReceiver implementations for common pointer types and references to the type, which allows using them as the receiver (first argument) in the msg_send! macro.

This trait also allows the object to be used in Retained.

This is a subtrait of RefEncode, meaning the type must also implement that, almost always with RefEncode::ENCODING_REF being Encoding::Object.

This can be implemented for unsized (!Sized) types, but the intention is not to support dynamically sized types like slices, only extern types (which is currently unstable).

§Drop interaction

If the inner type implements Drop, that implementation will very likely not be called, since there is no way to ensure that the Objective-C runtime will do so. If you need to run some code when the object is destroyed, implement the dealloc method instead.

The define_class! macro does this for you, but the extern_class! macro fundamentally cannot.

§Safety

The type must represent an Objective-C object, meaning it:

  • Must be valid to reinterpret as AnyObject.
  • Must be able to be the receiver of an Objective-C message sent with objc_msgSend or similar.
  • Must respond to the standard memory management retain, release and autorelease messages.
  • Must support weak references. (In the future we should probably make a new trait for this, for example NSTextView only supports weak references on macOS 10.12 or above).

§Example

use objc2::runtime::NSObject;
use objc2::{Encoding, Message, RefEncode};

#[repr(C)]
struct MyObject {
    // This has the exact same layout as `NSObject`
    inner: NSObject
}

unsafe impl RefEncode for MyObject {
    const ENCODING_REF: Encoding = Encoding::Object;
}

unsafe impl Message for MyObject {}

// `*mut MyObject` and other pointer/reference types to the object can
// now be used in `msg_send!`
//
// And `Retained<MyObject>` can now be constructed.

Implement the trait manually for a class with a lifetime parameter.

//! Note: We can't use the `define_class!` macro for this, it doesn't support
//! such use-cases (yet). Instead, we'll create it manually.
#![deny(unsafe_op_in_unsafe_fn)]
use std::cell::Cell;
use std::marker::PhantomData;
use std::sync::Once;

use objc2::rc::Retained;
use objc2::runtime::{AnyClass, AnyObject, ClassBuilder, NSObject};
use objc2::{msg_send, ClassType, Encoding, Message, RefEncode};

/// The type of the instance variable that we want to store
type Ivar<'a> = &'a Cell<u8>;

/// Struct that represents our custom object.
#[repr(C)]
struct MyObject<'a> {
    /// Required to give MyObject the proper layout
    ///
    /// Beware: `retain`-ing this field directly is dangerous, since it'd make
    /// it possible to extend the lifetime of the object beyond lifetime `'a`.
    inner: AnyObject,
    /// For auto traits and variance
    p: PhantomData<Ivar<'a>>,
}

unsafe impl RefEncode for MyObject<'_> {
    const ENCODING_REF: Encoding = NSObject::ENCODING_REF;
}

unsafe impl Message for MyObject<'_> {}

impl<'a> MyObject<'a> {
    fn class() -> &'static AnyClass {
        // NOTE: Use std::lazy::LazyCell if in MSRV
        static REGISTER_CLASS: Once = Once::new();

        REGISTER_CLASS.call_once(|| {
            let superclass = NSObject::class();
            let mut builder = ClassBuilder::new(c"MyObject", superclass).unwrap();

            builder.add_ivar::<Ivar<'_>>(c"number");

            let _cls = builder.register();
        });
        AnyClass::get(c"MyObject").unwrap()
    }

    fn new(number: &'a mut u8) -> Retained<Self> {
        // SAFETY: The instance variable is initialized below.
        let this: Retained<Self> = unsafe { msg_send![Self::class(), new] };

        // It is generally very hard to use `mut` in Objective-C, so let's use
        // interior mutability instead.
        let number = Cell::from_mut(number);

        let ivar = Self::class().instance_variable(c"number").unwrap();
        // SAFETY: The ivar is added with the same type below, and the
        // lifetime of the reference is properly bound to the class.
        unsafe { ivar.load_ptr::<Ivar<'_>>(&this.inner).write(number) };
        this
    }

    fn get(&self) -> u8 {
        let ivar = Self::class().instance_variable(c"number").unwrap();
        // SAFETY: The ivar is added with the same type below, and is initialized in `new`
        unsafe { ivar.load::<Ivar<'_>>(&self.inner).get() }
    }

    fn set(&self, number: u8) {
        let ivar = Self::class().instance_variable(c"number").unwrap();
        // SAFETY: The ivar is added with the same type below, and is initialized in `new`
        unsafe { ivar.load::<Ivar<'_>>(&self.inner).set(number) };
    }
}

fn main() {
    let mut number = 54;

    let obj = MyObject::new(&mut number);
    assert_eq!(obj.get(), 54);

    // We can now mutate the referenced `number`
    obj.set(7);
    assert_eq!(obj.get(), 7);

    // And we can clone the object, since we use interior mutability.
    let clone = obj.clone();
    clone.set(42);
    assert_eq!(obj.get(), 42);
    drop(clone);

    // It is not possible to convert to `Retained<NSObject>`, since that would
    // loose the lifetime information that `MyObject` stores.
    //
    // let obj = obj.into_super();
    //
    // Neither is it not possible to access `number` any more, since `obj`
    // holds a mutable reference to it.
    //
    // assert_eq!(number, 42);

    drop(obj);
    // And now that we've dropped `obj`, we can access `number` again
    assert_eq!(number, 42);
}

Provided Methods§

Source

fn retain(&self) -> Retained<Self>
where Self: Sized,

Increment the reference count of the receiver.

This extends the duration in which the receiver is alive by detaching it from the lifetime information carried by the reference.

This is similar to using Clone on Retained<Self>, with the addition that it can be used on a plain reference.

If your type may have come from a mutable type like NSMutableString, you should consider using NSCopying::copy instead to avoid carrying around a mutable string when you did not intend to.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§

Source§

impl Message for Exception

Source§

impl Message for AnyClass

Source§

impl Message for AnyObject

Source§

impl Message for AnyProtocol

Note that protocols are objects, though sending messages to them is officially deprecated.

Source§

impl Message for NSObject

Source§

impl<P: ?Sized> Message for ProtocolObject<P>