diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index 55d8c12dc..ff1432efe 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). (default) feature flag `"foundation"`. * Added `declare_class!`, `extern_class!` and `ns_string!` macros from `objc2-foundation`. +* Added helper method `ClassBuilder::add_static_ivar`. ### Changed * **BREAKING**: Change selector syntax in `declare_class!` macro to be more Rust-like. diff --git a/objc2/examples/class_with_lifetime.rs b/objc2/examples/class_with_lifetime.rs index bbd4ac481..0309ec3e7 100644 --- a/objc2/examples/class_with_lifetime.rs +++ b/objc2/examples/class_with_lifetime.rs @@ -71,7 +71,7 @@ impl<'a> MyObject<'a> { let superclass = NSObject::class(); let mut builder = ClassBuilder::new("MyObject", superclass).unwrap(); - builder.add_ivar::< as IvarType>::Type>(>::NAME); + builder.add_static_ivar::>(); /// Helper struct since we can't access the instance variable /// from inside MyObject, since it hasn't been initialized yet! diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index 36088c688..2a5ed5350 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -402,9 +402,11 @@ impl ClassBuilder { /// Adds an ivar with type `T` and the provided name. /// + /// /// # Panics /// - /// If the ivar wasn't successfully added. + /// If the ivar wasn't successfully added for some reason - this usually + /// happens if there already was an ivar with that name. pub fn add_ivar(&mut self, name: &str) { let c_name = CString::new(name).unwrap(); let encoding = CString::new(T::ENCODING.to_string()).unwrap(); @@ -422,6 +424,16 @@ impl ClassBuilder { assert!(success.as_bool(), "Failed to add ivar {}", name); } + /// Adds an instance variable from an [`IvarType`]. + /// + /// + /// # Panics + /// + /// Same as [`ClassBuilder::add_ivar`]. + pub fn add_static_ivar(&mut self) { + self.add_ivar::(T::NAME); + } + /// Adds the given protocol to self. /// /// # Panics diff --git a/objc2/src/declare/ivar.rs b/objc2/src/declare/ivar.rs index a1baeaf0f..f65e740b9 100644 --- a/objc2/src/declare/ivar.rs +++ b/objc2/src/declare/ivar.rs @@ -72,10 +72,14 @@ unsafe impl IvarType for MaybeUninit { /// particular, this is never safe to have on the stack by itself. /// /// Additionally, the instance variable described by `T` must be available on -/// the specific instance, and be of the exact same type. +/// the specific instance, and be of the exact same type. When declaring the +/// object yourself, you can ensure this using +/// [`ClassBuilder::add_static_ivar`]. /// /// Finally, two ivars with the same name must not be used on the same object. /// +/// [`ClassBuilder::add_static_ivar`]: crate::declare::ClassBuilder::add_static_ivar +/// /// /// # Examples /// @@ -106,7 +110,7 @@ unsafe impl IvarType for MaybeUninit { /// # use objc2::declare::ClassBuilder; /// # let mut builder = ClassBuilder::new("MyObject", class!(NSObject)).unwrap(); /// // Declare the class and add the instance variable to it -/// builder.add_ivar::<::Type>(MyCustomIvar::NAME); +/// builder.add_static_ivar::(); /// # let _cls = builder.register(); /// /// let obj: MyObject; @@ -123,11 +127,8 @@ pub struct Ivar { item: PhantomData, } -impl Deref for Ivar { - type Target = T::Type; - - #[inline] - fn deref(&self) -> &Self::Target { +impl Ivar { + fn get_ref(&self) -> &T::Type { // SAFETY: The user ensures that this is placed in a struct that can // be reinterpreted as an `Object`. Since `Ivar` can never be // constructed by itself (and is neither Copy nor Clone), we know that @@ -146,13 +147,10 @@ impl Deref for Ivar { // exists and has the correct type unsafe { obj.ivar::(T::NAME) } } -} -impl DerefMut for Ivar { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { + fn get_mut_ptr(&mut self) -> *mut T::Type { let ptr = NonNull::from(self).cast::(); - // SAFETY: Same as `deref`. + // SAFETY: Same as `get_ref`. // // Note: We don't use `mut` because the user might have two mutable // references to different ivars, as such: @@ -177,12 +175,33 @@ impl DerefMut for Ivar { // this is definitely safe. let obj = unsafe { ptr.as_ref() }; - // SAFETY: Same as `deref` - // - // Safe as mutable because there is only one access to a particular - // ivar at a time (since we have `&mut self`). `Object` is + // SAFETY: User ensures that the `Ivar` is only used when the ivar + // exists and has the correct type + unsafe { obj.ivar_ptr::(T::NAME) } + } + + #[inline] + fn get_mut(&mut self) -> &mut T::Type { + // SAFETY: Safe as mutable because there is only one access to a + // particular ivar at a time (since we have `&mut self`). `Object` is // `UnsafeCell`, so mutable access through `&Object` is allowed. - unsafe { obj.ivar_ptr::(T::NAME).as_mut().unwrap_unchecked() } + unsafe { self.get_mut_ptr().as_mut().unwrap_unchecked() } + } +} + +impl Deref for Ivar { + type Target = T::Type; + + #[inline] + fn deref(&self) -> &Self::Target { + self.get_ref() + } +} + +impl DerefMut for Ivar { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.get_mut() } } diff --git a/objc2/src/macros/declare_class.rs b/objc2/src/macros/declare_class.rs index ce1fcb7d3..ab35b81c4 100644 --- a/objc2/src/macros/declare_class.rs +++ b/objc2/src/macros/declare_class.rs @@ -495,8 +495,7 @@ macro_rules! declare_class { unsafe $v struct $name<>: $inherits, $($inheritance_rest,)* $crate::runtime::Object { // SAFETY: // - The ivars are in a type used as an Objective-C object. - // - The instance variable is defined in the exact same manner - // in `class` below. + // - The ivar is added to the class below. // - Rust prevents having two fields with the same name. // - Caller upholds that the ivars are properly initialized. $($ivar_v $ivar: $crate::declare::Ivar<$ivar>,)* @@ -527,10 +526,9 @@ macro_rules! declare_class { ); let mut builder = $crate::declare::ClassBuilder::new(stringify!($name), superclass).expect(err_str); + // Ivars $( - builder.add_ivar::<<$ivar as $crate::declare::IvarType>::Type>( - <$ivar as $crate::declare::IvarType>::NAME - ); + builder.add_static_ivar::<$ivar>(); )* $(