Expand description
Functionality for dynamically declaring Objective-C classes.
Classes can be declared using the ClassBuilder
struct. Instance
variables and methods can then be added before the class is ultimately
registered.
Note: You likely don’t need the dynamicism that this module provides!
Consider using the declare_class!
macro instead.
Example
The following example demonstrates declaring a class named MyNumber
that
has one ivar, a u32
named _number
and a few methods for constructor
methods and methods for interfacing with the number.
use objc2::declare::ClassBuilder;
use objc2::foundation::NSObject;
use objc2::rc::{Id, Owned};
use objc2::runtime::{Class, Object, Sel};
use objc2::{class, sel, msg_send, msg_send_id, ClassType};
fn register_class() -> &'static Class {
// Inherit from NSObject
let mut builder = ClassBuilder::new("MyNumber", NSObject::class())
.expect("a class with the name MyNumber likely already exists");
// Add an instance variable of type `u32`
builder.add_ivar::<u32>("_number");
// Add an Objective-C method for initializing an instance with a number
unsafe extern "C" fn init_with_number(
this: &mut Object,
_cmd: Sel,
number: u32,
) -> Option<&mut Object> {
let this: Option<&mut Object> = msg_send![super(this, NSObject::class()), init];
this.map(|this| {
// SAFETY: The ivar is added with the same type above
this.set_ivar::<u32>("_number", number);
this
})
}
unsafe {
builder.add_method(
sel!(initWithNumber:),
init_with_number as unsafe extern "C" fn(_, _, _) -> _,
);
}
// Add convenience method for getting a new instance with the number
extern "C" fn with_number(
cls: &Class,
_cmd: Sel,
number: u32,
) -> *mut Object {
let obj: Option<Id<Object, Owned>> = unsafe {
msg_send_id![
msg_send_id![cls, alloc],
initWithNumber: number,
]
};
obj.map(|obj| obj.autorelease_return()).unwrap_or(std::ptr::null_mut())
}
unsafe {
builder.add_class_method(
sel!(withNumber:),
with_number as extern "C" fn(_, _, _) -> _,
);
}
// Add an Objective-C method for setting the number
extern "C" fn my_number_set(this: &mut Object, _cmd: Sel, number: u32) {
// SAFETY: The ivar is added with the same type above
unsafe { this.set_ivar::<u32>("_number", number) }
}
unsafe {
builder.add_method(sel!(setNumber:), my_number_set as extern "C" fn(_, _, _));
}
// Add an Objective-C method for getting the number
extern "C" fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
// SAFETY: The ivar is added with the same type above
unsafe { *this.ivar::<u32>("_number") }
}
unsafe {
builder.add_method(sel!(number), my_number_get as extern "C" fn(_, _) -> _);
}
builder.register()
}
// Usage
// Note: you should only do class registration once! This can be ensure
// with `std::sync::Once` or the `once_cell` crate.
let cls = register_class();
let obj: Id<Object, Owned> = unsafe {
msg_send_id![cls, withNumber: 42u32]
};
let n: u32 = unsafe { msg_send![&obj, number] };
assert_eq!(n, 42);
let _: () = unsafe { msg_send![&obj, setNumber: 12u32] };
let n: u32 = unsafe { msg_send![&obj, number] };
assert_eq!(n, 12);
Structs
- A type for declaring a new class and adding new methods and ivars to it before registering it.
- A wrapper type over a custom instance variable.
- A helper type to allow putting certain types that may drop into ivars.
- A type for declaring a new protocol and adding new methods to it before registering it.
Traits
- Types that may be used in ivars.
- Helper trait for defining instance variables.
- Types that can be used as the implementation of an Objective-C method.