Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

declare_class! macro #190

Merged
merged 21 commits into from
Jul 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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