Trait objc2::ClassType

source ·
pub unsafe trait ClassType: Message {
    type Super: Message;
    type Mutability: Mutability;

    const NAME: &'static str;

    // Required methods
    fn class() -> &'static AnyClass;
    fn as_super(&self) -> &Self::Super;
    fn as_super_mut(&mut self) -> &mut Self::Super;

    // Provided methods
    fn retain(&self) -> Id<Self> 
       where Self: IsRetainable + Sized { ... }
    fn alloc() -> Allocated<Self>
       where Self: IsAllocableAnyThread + Sized { ... }
}
Expand description

Marks types that represent specific classes.

Sometimes it is enough to generically know that a type is messageable, e.g. rc::Id works with any type that implements the Message trait. But often, you have an object that you know represents a specific Objective-C class - this trait allows you to communicate that, as well as a few properties of the class to the rest of the type-system.

This is implemented automatically for your type by the declare_class! and extern_class! macros.

Safety

  1. The type must represent a specific class.

  2. Self::Super must be a superclass of the class (or something that represents any object, like AnyObject).

  3. Self::Mutability must be specified correctly.

    Note that very little Objective-C code follows Rust’s usual ownership model. If you think your type’s mutability should be Mutable, think again, it very rarely should!

    If you’re unsure of what to do, InteriorMutable + avoiding &mut is usually a good starting point.

  4. Self::NAME must be correct.

  5. The class returned by Self::class must be the class that this type represents.

Examples

Use the trait to access the AnyClass of an object.

use objc2::{ClassType, msg_send_id};
use objc2::rc::Id;

// Get the class of the object.
let cls = <MyObject as ClassType>::class();
// Or, since the trait is in scope.
let cls = MyObject::class();

// We can now access properties of the class.
assert_eq!(cls.name(), MyObject::NAME);

// And we can send messages to the class.
//
// SAFETY:
// - The class is `MyObject`, which can safely be initialized with `new`.
// - The return type is correctly specified.
let obj: Id<MyObject> = unsafe { msg_send_id![cls, new] };

Use the trait to allocate a new instance of an object.

use objc2::{ClassType, msg_send_id};
use objc2::rc::Id;

let obj = MyObject::alloc();

// Now we can call initializers on this newly allocated object.
//
// SAFETY: `MyObject` can safely be initialized with `init`.
let obj: Id<MyObject> = unsafe { msg_send_id![obj, init] };

Use the extern_class! macro to implement this trait for a type.

use objc2::runtime::NSObject;
use objc2::{extern_class, mutability, ClassType};

extern_class!(
    struct MyClass;

    // SAFETY: The superclass and the mutability is correctly specified.
    unsafe impl ClassType for MyClass {
        type Super = NSObject;
        type Mutability = mutability::InteriorMutable;
    }
);

let cls = MyClass::class();
let obj = MyClass::alloc();

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

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

use objc2::declare::ClassBuilder;
use objc2::mutability::Mutable;
use objc2::rc::Id;
use objc2::runtime::{AnyClass, NSObject, Sel};
use objc2::{msg_send, msg_send_id, sel};
use objc2::{ClassType, Encoding, Message, RefEncode};

/// Struct that represents our custom object.
#[repr(C)]
pub struct MyObject<'a> {
    // Required to give MyObject the proper layout
    superclass: NSObject,
    p: PhantomData<&'a mut u8>,
}

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

unsafe impl Message for MyObject<'_> {}

impl<'a> MyObject<'a> {
    unsafe extern "C" fn init_with_ptr<'s>(
        &'s mut self,
        _cmd: Sel,
        ptr: Option<&'a mut u8>,
    ) -> Option<&'s mut Self> {
        let this: Option<&mut Self> = unsafe { msg_send![super(self), init] };
        this.map(|this| {
            let ivar = Self::class().instance_variable("number").unwrap();
            // SAFETY: The ivar is added with the same type below
            unsafe {
                ivar.load_ptr::<&mut u8>(&this.superclass)
                    .write(ptr.expect("got NULL number ptr"))
            };
            this
        })
    }

    pub fn new(number: &'a mut u8) -> Id<Self> {
        // SAFETY: The lifetime of the reference is properly bound to the
        // returned type
        unsafe { msg_send_id![Self::alloc(), initWithPtr: number] }
    }

    pub fn get(&self) -> u8 {
        let ivar = Self::class().instance_variable("number").unwrap();
        // SAFETY: The ivar is added with the same type below, and is initialized in `init_with_ptr`
        unsafe { **ivar.load::<&mut u8>(&self.superclass) }
    }

    pub fn set(&mut self, number: u8) {
        let ivar = Self::class().instance_variable("number").unwrap();
        // SAFETY: The ivar is added with the same type below, and is initialized in `init_with_ptr`
        unsafe { **ivar.load_mut::<&mut u8>(&mut self.superclass) = number };
    }
}

unsafe impl<'a> ClassType for MyObject<'a> {
    type Super = NSObject;
    type Mutability = Mutable;
    const NAME: &'static str = "MyObject";

    fn class() -> &'static AnyClass {
        // TODO: Use std::lazy::LazyCell
        static REGISTER_CLASS: Once = Once::new();

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

            builder.add_ivar::<&mut u8>("number");

            unsafe {
                builder.add_method(
                    sel!(initWithPtr:),
                    Self::init_with_ptr as unsafe extern "C" fn(_, _, _) -> _,
                );
            }

            let _cls = builder.register();
        });

        AnyClass::get("MyObject").unwrap()
    }

    fn as_super(&self) -> &Self::Super {
        &self.superclass
    }

    fn as_super_mut(&mut self) -> &mut Self::Super {
        &mut self.superclass
    }
}

fn main() {
    let mut number = 54;

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

    // It is not possible to convert to `Id<NSObject>`, since that would loose
    // the lifetime information that `MyObject` stores.
    //
    // let obj = Id::into_super(obj);
    //
    // Neither is it possible to clone or retain the object, since it is
    // marked as `Mutable` in `ClassType::Mutability`.
    //
    // let obj2 = obj.clone();
    //
    // Finally, it is not possible to access `number` any more, since `obj`
    // holds a mutable reference to it.
    //
    // assert_eq!(number, 7);

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

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

Required Associated Types§

source

type Super: Message

The superclass of this class.

If you have implemented Deref for your type, it is highly recommended that this is equal to Deref::Target.

This may be AnyObject if the class is a root class.

source

type Mutability: Mutability

Whether the type is mutable or immutable.

See the mutability module for further details about class mutability.

Required Associated Constants§

source

const NAME: &'static str

The name of the Objective-C class that this type represents.

T::NAME is the const version of T::class().name().

Required Methods§

source

fn class() -> &'static AnyClass

Get a reference to the Objective-C class that this type represents.

May register the class with the runtime if it wasn’t already.

Panics

This may panic if something went wrong with getting or declaring the class, e.g. if the program is not properly linked to the framework that defines the class.

source

fn as_super(&self) -> &Self::Super

Get an immutable reference to the superclass.

source

fn as_super_mut(&mut self) -> &mut Self::Super

Get a mutable reference to the superclass.

Provided Methods§

source

fn retain(&self) -> Id<Self>
where Self: IsRetainable + 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 Id<Self>, with the addition that it can be used on a plain reference. Note however that this is not possible to use on certain types like NSString, since if you only hold &NSString, that may have come from &mut NSMutableString, in which case it would be unsound to erase the lifetime information carried by the reference.

In cases like that, you should rather use NSCopying::copy (since that gives you a NSString whether the string was originally a NSString or a NSMutableString).

source

fn alloc() -> Allocated<Self>

Allocate a new instance of the class.

The return value can be used directly inside msg_send_id! to initialize the object.

For classes that are only usable on the main thread, you can use MainThreadMarker::alloc instead.

Object Safety§

This trait is not object safe.

Implementors§

source§

impl ClassType for NSObject

§

type Super = AnyObject

§

type Mutability = Root

source§

const NAME: &'static str = "NSObject"