Skip to content

Commit

Permalink
Merge pull request #190 from madsmtm/declare-macro
Browse files Browse the repository at this point in the history
Add `declare_class!` macro
  • Loading branch information
madsmtm committed Jul 17, 2022
2 parents 1dcf0d7 + 1ac9617 commit bbafcf3
Show file tree
Hide file tree
Showing 13 changed files with 1,643 additions and 30 deletions.
4 changes: 3 additions & 1 deletion objc2-foundation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Added `MainThreadMarker` to help with designing APIs where a method is only
safe to call on the main thread.
* Added `NSException` object.
* Added `extern_class!` macro to help with defining other classes.
* Added `extern_class!` macro to help with defining interfaces to classes.
* Added `declare_class!` macro to help with declaring custom classes.
* Expose the `objc2` version that this uses in the crate root.
* Added `NSZone`.

### Changed
* Changed a few `Debug` impls.
Expand Down
29 changes: 13 additions & 16 deletions objc2-foundation/examples/custom_class.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
use std::sync::Once;

use objc2::declare::ClassBuilder;
use objc2::declare::{ClassBuilder, Ivar, IvarType};
use objc2::rc::{Id, Owned};
use objc2::runtime::{Class, Object, Sel};
use objc2::{msg_send, msg_send_id, sel};
use objc2::{Encoding, Message, RefEncode};
use objc2_foundation::NSObject;

/// In the future this should be an `extern type`, if that gets stabilized,
/// see [RFC-1861](https://rust-lang.github.io/rfcs/1861-extern-types.html).
struct NumberIvar;
unsafe impl IvarType for NumberIvar {
type Type = u32;
const NAME: &'static str = "_number";
}

#[repr(C)]
pub struct MYObject {
inner: Object,
number: Ivar<NumberIvar>,
}

unsafe impl RefEncode for MYObject {
Expand All @@ -28,27 +33,19 @@ impl MYObject {
unsafe { msg_send_id![cls, new].unwrap() }
}

fn number(&self) -> u32 {
unsafe { *self.inner.ivar("_number") }
}

fn set_number(&mut self, number: u32) {
unsafe { self.inner.set_ivar("_number", number) };
}

fn class() -> &'static Class {
MYOBJECT_REGISTER_CLASS.call_once(|| {
let superclass = NSObject::class();
let mut builder = ClassBuilder::new("MYObject", superclass).unwrap();
builder.add_ivar::<u32>("_number");
builder.add_ivar::<<NumberIvar as IvarType>::Type>(<NumberIvar as IvarType>::NAME);

// Add ObjC methods for getting and setting the number
extern "C" fn my_object_set_number(this: &mut MYObject, _cmd: Sel, number: u32) {
this.set_number(number);
*this.number = number;
}

extern "C" fn my_object_get_number(this: &MYObject, _cmd: Sel) -> u32 {
this.number()
*this.number
}

unsafe {
Expand All @@ -68,7 +65,7 @@ impl MYObject {
fn main() {
let mut obj = MYObject::new();

obj.set_number(7);
*obj.number = 7;
println!("Number: {}", unsafe {
let number: u32 = msg_send![&obj, number];
number
Expand All @@ -77,5 +74,5 @@ fn main() {
unsafe {
let _: () = msg_send![&mut obj, setNumber: 12u32];
}
println!("Number: {}", obj.number());
println!("Number: {}", obj.number);
}
83 changes: 83 additions & 0 deletions objc2-foundation/examples/declaration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use objc2::{
msg_send, msg_send_id,
rc::{Id, Shared},
runtime::{Bool, Object},
};
use objc2_foundation::{declare_class, extern_class, NSObject};

#[cfg(all(feature = "apple", target_os = "macos"))]
#[link(name = "AppKit", kind = "framework")]
extern "C" {}

extern_class! {
unsafe struct NSResponder: NSObject;
}

declare_class! {
unsafe struct CustomAppDelegate: NSResponder, NSObject {
pub ivar: u8,
another_ivar: Bool,
}

unsafe impl {
@sel(initWith:another:)
fn init_with(
self: &mut Self,
ivar: u8,
another_ivar: Bool,
) -> *mut Self {
let this: *mut Self = unsafe {
msg_send![super(self, NSResponder::class()), init]
};
if let Some(this) = unsafe { this.as_mut() } {
// TODO: Allow initialization through MaybeUninit
*this.ivar = ivar;
*this.another_ivar = another_ivar;
}
this
}

@sel(myClassMethod)
fn my_class_method() {
println!("A class method!");
}
}

// For some reason, `NSApplicationDelegate` is not a "real" protocol we
// can retrieve using `objc_getProtocol` - it seems it is created by
// `clang` only when used in Objective-C...
//
// TODO: Investigate this!
unsafe impl {
@sel(applicationDidFinishLaunching:)
fn did_finish_launching(&self, _sender: *mut Object) {
println!("Did finish launching!");
}

@sel(applicationWillTerminate:)
fn will_terminate(&self, _: *mut Object) {
println!("Will terminate!");
}
}
}

impl CustomAppDelegate {
pub fn new(ivar: u8, another_ivar: bool) -> Id<Self, Shared> {
let cls = Self::class();
unsafe {
msg_send_id![
msg_send_id![cls, alloc],
initWith: ivar,
another: Bool::from(another_ivar),
]
.unwrap()
}
}
}

fn main() {
let delegate = CustomAppDelegate::new(42, true);

println!("{}", delegate.ivar);
println!("{}", delegate.another_ivar.as_bool());
}
Loading

0 comments on commit bbafcf3

Please sign in to comment.