Skip to content

Commit 8fdab4a

Browse files
committed
Add support for storing Id and Box in ivars
1 parent 2889cbe commit 8fdab4a

File tree

7 files changed

+550
-44
lines changed

7 files changed

+550
-44
lines changed

objc2/CHANGELOG.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88
## Unreleased - YYYY-MM-DD
99

1010
### Added
11-
* `Ivar::write`, `Ivar::as_ptr` and `Ivar::as_mut_ptr` for querying/modifying
12-
the instance variable inside `init` methods.
11+
* Added `Ivar::write`, `Ivar::as_ptr` and `Ivar::as_mut_ptr` for safely
12+
querying and modifying instance variables inside `init` methods.
13+
* Added `IvarDrop<T>` to allow storing complex `Drop` values in ivars
14+
(currently `rc::Id<T, O>`, `Box<T>`, `Option<rc::Id<T, O>>` or
15+
`Option<Box<T>>`).
1316

1417
### Removed
1518
* **BREAKING**: `MaybeUninit` no longer implements `IvarType` directly; use

objc2/examples/delegate.rs

+33-15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#![cfg_attr(not(all(feature = "apple", target_os = "macos")), allow(unused))]
2-
use objc2::declare::Ivar;
3-
use objc2::foundation::NSObject;
2+
use objc2::declare::{Ivar, IvarDrop};
3+
use objc2::foundation::{NSCopying, NSObject, NSString};
44
use objc2::rc::{Id, Shared};
55
use objc2::runtime::Object;
6-
use objc2::{declare_class, extern_class, msg_send, msg_send_id, ClassType};
6+
use objc2::{declare_class, extern_class, msg_send, msg_send_id, ns_string, ClassType};
77

88
#[cfg(all(feature = "apple", target_os = "macos"))]
99
#[link(name = "AppKit", kind = "framework")]
@@ -23,6 +23,10 @@ declare_class!(
2323
struct CustomAppDelegate {
2424
pub ivar: u8,
2525
another_ivar: bool,
26+
box_ivar: IvarDrop<Box<i32>>,
27+
maybe_box_ivar: IvarDrop<Option<Box<i32>>>,
28+
id_ivar: IvarDrop<Id<NSString, Shared>>,
29+
maybe_id_ivar: IvarDrop<Option<Id<NSString, Shared>>>,
2630
}
2731

2832
unsafe impl ClassType for CustomAppDelegate {
@@ -34,18 +38,28 @@ declare_class!(
3438
#[sel(initWith:another:)]
3539
fn init_with(self: &mut Self, ivar: u8, another_ivar: bool) -> Option<&mut Self> {
3640
let this: Option<&mut Self> = unsafe { msg_send![super(self), init] };
41+
42+
// TODO: `ns_string` can't be used inside closures; investigate!
43+
let s = ns_string!("def");
44+
3745
this.map(|this| {
46+
// Initialize instance variables
47+
48+
// The explicit way to initialize an instance variable:
3849
Ivar::write(&mut this.ivar, ivar);
39-
Ivar::write(&mut this.another_ivar, another_ivar);
40-
// Note that we could have done this with just:
41-
// *this.ivar = ivar;
42-
// *this.another_ivar = another_ivar;
43-
//
44-
// Since these two ivar types (`u8` and `bool`) are safe to
45-
// initialize from all zeroes; but for this example, we chose
46-
// to be explicit.
47-
48-
// SAFETY: All the instance variables have been initialized
50+
// For some types like `u8`, `bool`, `Option<Box<T>>` and
51+
// `Option<Id<T, O>>` which are safe to zero-initialize, we
52+
// can simply write to the variable as normal:
53+
*this.another_ivar = another_ivar;
54+
*this.maybe_box_ivar = None;
55+
*this.maybe_id_ivar = Some(s.copy());
56+
// While for others like `&u8`, `Box<T>` or `Id<T, O>`, we
57+
// have to initialize it with `Ivar::write`:
58+
Ivar::write(&mut this.box_ivar, Box::new(2));
59+
Ivar::write(&mut this.id_ivar, NSString::from_str("abc"));
60+
61+
// All the instance variables have been initialized; our
62+
// initializer is sound
4963
this
5064
})
5165
}
@@ -97,8 +111,12 @@ impl CustomAppDelegate {
97111
fn main() {
98112
let delegate = CustomAppDelegate::new(42, true);
99113

100-
println!("{}", delegate.ivar);
101-
println!("{}", delegate.another_ivar);
114+
println!("{:?}", delegate.ivar);
115+
println!("{:?}", delegate.another_ivar);
116+
println!("{:?}", delegate.box_ivar);
117+
println!("{:?}", delegate.maybe_box_ivar);
118+
println!("{:?}", delegate.id_ivar);
119+
println!("{:?}", delegate.maybe_id_ivar);
102120
}
103121

104122
#[cfg(not(all(feature = "apple", target_os = "macos")))]

objc2/src/__macro_helpers.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub use core::mem::size_of;
1313
pub use core::ops::{Deref, DerefMut};
1414
pub use core::option::Option::{self, None, Some};
1515
pub use core::primitive::{bool, str, u8};
16+
pub use core::ptr::drop_in_place;
1617
pub use core::{compile_error, concat, panic, stringify};
1718
// TODO: Use `core::cell::LazyCell`
1819
pub use std::sync::Once;

objc2/src/declare.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
//! ```
113113
114114
mod ivar;
115+
mod ivar_drop;
115116
mod ivar_forwarding_impls;
116117

117118
use alloc::format;
@@ -122,13 +123,14 @@ use core::ptr;
122123
use core::ptr::NonNull;
123124
use std::ffi::CString;
124125

125-
use crate::encode::{Encode, EncodeArguments, EncodeConvert, Encoding, RefEncode};
126+
use crate::encode::{Encode, EncodeArguments, Encoding, RefEncode};
126127
use crate::ffi;
127128
use crate::runtime::{Bool, Class, Imp, Object, Protocol, Sel};
128129
use crate::sel;
129130
use crate::Message;
130131

131-
pub use ivar::{Ivar, IvarType};
132+
pub use ivar::{InnerIvarType, Ivar, IvarType};
133+
pub use ivar_drop::IvarDrop;
132134

133135
pub(crate) mod private {
134136
pub trait Sealed {}
@@ -439,7 +441,12 @@ impl ClassBuilder {
439441
/// Same as [`ClassBuilder::add_ivar`].
440442
pub fn add_static_ivar<T: IvarType>(&mut self) {
441443
// SAFETY: The encoding is correct
442-
unsafe { self.add_ivar_inner::<T::Type>(T::NAME, &T::Type::__ENCODING) }
444+
unsafe {
445+
self.add_ivar_inner::<<T::Type as InnerIvarType>::__Inner>(
446+
T::NAME,
447+
&T::Type::__ENCODING,
448+
)
449+
}
443450
}
444451

445452
/// Adds the given protocol to self.

0 commit comments

Comments
 (0)