Skip to content

Commit 6f68cd0

Browse files
authored
Merge pull request #233 from madsmtm/add-static-ivar
Add `ClassBuilder::add_static_ivar`
2 parents e5919bb + d9f9b32 commit 6f68cd0

File tree

5 files changed

+54
-24
lines changed

5 files changed

+54
-24
lines changed

objc2/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1414
(default) feature flag `"foundation"`.
1515
* Added `declare_class!`, `extern_class!` and `ns_string!` macros from
1616
`objc2-foundation`.
17+
* Added helper method `ClassBuilder::add_static_ivar`.
1718

1819
### Changed
1920
* **BREAKING**: Change selector syntax in `declare_class!` macro to be more Rust-like.

objc2/examples/class_with_lifetime.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl<'a> MyObject<'a> {
7171
let superclass = NSObject::class();
7272
let mut builder = ClassBuilder::new("MyObject", superclass).unwrap();
7373

74-
builder.add_ivar::<<NumberIvar<'a> as IvarType>::Type>(<NumberIvar<'a>>::NAME);
74+
builder.add_static_ivar::<NumberIvar<'a>>();
7575

7676
/// Helper struct since we can't access the instance variable
7777
/// from inside MyObject, since it hasn't been initialized yet!

objc2/src/declare.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -402,9 +402,11 @@ impl ClassBuilder {
402402

403403
/// Adds an ivar with type `T` and the provided name.
404404
///
405+
///
405406
/// # Panics
406407
///
407-
/// If the ivar wasn't successfully added.
408+
/// If the ivar wasn't successfully added for some reason - this usually
409+
/// happens if there already was an ivar with that name.
408410
pub fn add_ivar<T: Encode>(&mut self, name: &str) {
409411
let c_name = CString::new(name).unwrap();
410412
let encoding = CString::new(T::ENCODING.to_string()).unwrap();
@@ -422,6 +424,16 @@ impl ClassBuilder {
422424
assert!(success.as_bool(), "Failed to add ivar {}", name);
423425
}
424426

427+
/// Adds an instance variable from an [`IvarType`].
428+
///
429+
///
430+
/// # Panics
431+
///
432+
/// Same as [`ClassBuilder::add_ivar`].
433+
pub fn add_static_ivar<T: IvarType>(&mut self) {
434+
self.add_ivar::<T::Type>(T::NAME);
435+
}
436+
425437
/// Adds the given protocol to self.
426438
///
427439
/// # Panics

objc2/src/declare/ivar.rs

+36-17
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,14 @@ unsafe impl<T: IvarType> IvarType for MaybeUninit<T> {
7272
/// particular, this is never safe to have on the stack by itself.
7373
///
7474
/// Additionally, the instance variable described by `T` must be available on
75-
/// the specific instance, and be of the exact same type.
75+
/// the specific instance, and be of the exact same type. When declaring the
76+
/// object yourself, you can ensure this using
77+
/// [`ClassBuilder::add_static_ivar`].
7678
///
7779
/// Finally, two ivars with the same name must not be used on the same object.
7880
///
81+
/// [`ClassBuilder::add_static_ivar`]: crate::declare::ClassBuilder::add_static_ivar
82+
///
7983
///
8084
/// # Examples
8185
///
@@ -106,7 +110,7 @@ unsafe impl<T: IvarType> IvarType for MaybeUninit<T> {
106110
/// # use objc2::declare::ClassBuilder;
107111
/// # let mut builder = ClassBuilder::new("MyObject", class!(NSObject)).unwrap();
108112
/// // Declare the class and add the instance variable to it
109-
/// builder.add_ivar::<<MyCustomIvar as IvarType>::Type>(MyCustomIvar::NAME);
113+
/// builder.add_static_ivar::<MyCustomIvar>();
110114
/// # let _cls = builder.register();
111115
///
112116
/// let obj: MyObject;
@@ -123,11 +127,8 @@ pub struct Ivar<T: IvarType> {
123127
item: PhantomData<T::Type>,
124128
}
125129

126-
impl<T: IvarType> Deref for Ivar<T> {
127-
type Target = T::Type;
128-
129-
#[inline]
130-
fn deref(&self) -> &Self::Target {
130+
impl<T: IvarType> Ivar<T> {
131+
fn get_ref(&self) -> &T::Type {
131132
// SAFETY: The user ensures that this is placed in a struct that can
132133
// be reinterpreted as an `Object`. Since `Ivar` can never be
133134
// constructed by itself (and is neither Copy nor Clone), we know that
@@ -146,13 +147,10 @@ impl<T: IvarType> Deref for Ivar<T> {
146147
// exists and has the correct type
147148
unsafe { obj.ivar::<T::Type>(T::NAME) }
148149
}
149-
}
150150

151-
impl<T: IvarType> DerefMut for Ivar<T> {
152-
#[inline]
153-
fn deref_mut(&mut self) -> &mut Self::Target {
151+
fn get_mut_ptr(&mut self) -> *mut T::Type {
154152
let ptr = NonNull::from(self).cast::<Object>();
155-
// SAFETY: Same as `deref`.
153+
// SAFETY: Same as `get_ref`.
156154
//
157155
// Note: We don't use `mut` because the user might have two mutable
158156
// references to different ivars, as such:
@@ -177,12 +175,33 @@ impl<T: IvarType> DerefMut for Ivar<T> {
177175
// this is definitely safe.
178176
let obj = unsafe { ptr.as_ref() };
179177

180-
// SAFETY: Same as `deref`
181-
//
182-
// Safe as mutable because there is only one access to a particular
183-
// ivar at a time (since we have `&mut self`). `Object` is
178+
// SAFETY: User ensures that the `Ivar<T>` is only used when the ivar
179+
// exists and has the correct type
180+
unsafe { obj.ivar_ptr::<T::Type>(T::NAME) }
181+
}
182+
183+
#[inline]
184+
fn get_mut(&mut self) -> &mut T::Type {
185+
// SAFETY: Safe as mutable because there is only one access to a
186+
// particular ivar at a time (since we have `&mut self`). `Object` is
184187
// `UnsafeCell`, so mutable access through `&Object` is allowed.
185-
unsafe { obj.ivar_ptr::<T::Type>(T::NAME).as_mut().unwrap_unchecked() }
188+
unsafe { self.get_mut_ptr().as_mut().unwrap_unchecked() }
189+
}
190+
}
191+
192+
impl<T: IvarType> Deref for Ivar<T> {
193+
type Target = T::Type;
194+
195+
#[inline]
196+
fn deref(&self) -> &Self::Target {
197+
self.get_ref()
198+
}
199+
}
200+
201+
impl<T: IvarType> DerefMut for Ivar<T> {
202+
#[inline]
203+
fn deref_mut(&mut self) -> &mut Self::Target {
204+
self.get_mut()
186205
}
187206
}
188207

objc2/src/macros/declare_class.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -495,8 +495,7 @@ macro_rules! declare_class {
495495
unsafe $v struct $name<>: $inherits, $($inheritance_rest,)* $crate::runtime::Object {
496496
// SAFETY:
497497
// - The ivars are in a type used as an Objective-C object.
498-
// - The instance variable is defined in the exact same manner
499-
// in `class` below.
498+
// - The ivar is added to the class below.
500499
// - Rust prevents having two fields with the same name.
501500
// - Caller upholds that the ivars are properly initialized.
502501
$($ivar_v $ivar: $crate::declare::Ivar<$ivar>,)*
@@ -527,10 +526,9 @@ macro_rules! declare_class {
527526
);
528527
let mut builder = $crate::declare::ClassBuilder::new(stringify!($name), superclass).expect(err_str);
529528

529+
// Ivars
530530
$(
531-
builder.add_ivar::<<$ivar as $crate::declare::IvarType>::Type>(
532-
<$ivar as $crate::declare::IvarType>::NAME
533-
);
531+
builder.add_static_ivar::<$ivar>();
534532
)*
535533

536534
$(

0 commit comments

Comments
 (0)