use core::fmt; use core::marker::PhantomData; use core::mem::ManuallyDrop; use core::ops::Deref; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::ptr::{self, NonNull};
/// A reference counted pointer type for Objective-C objects. /// /// [`Retained`] strongly references or "retains" the given object `T`, and /// decrements the retain count or "releases" it again when dropped, thereby /// ensuring it will be deallocated at the right time. /// /// The type `T` inside `Retained<T>` can be anything that implements /// [`Message`]. /// /// This can usually be gotten from one of the methods in [the framework /// crates], but can also be created manually with the [`msg_send!`] macro, or /// even more manually with the [`Retained::retain`], [`Retained::from_raw`] /// and [`Retained::retain_autoreleased`] methods. /// /// [the framework crates]: crate::topics::about_generated /// [`msg_send!`]: crate::msg_send /// /// /// # Comparison to `std` types /// /// `Retained<T>` is the Objective-C equivalent of [`Arc`], that is, it is a /// thread-safe reference-counting pointer, and allows cloning by bumping the /// reference count, and weak references using [`rc::Weak`]. /// /// Unlike `Arc`, objects can be retained directly from a `&T` using /// [`Message::retain`] (for `Arc` you need `&Arc<T>`). /// /// Even though most Objective-C types aren't thread safe, Objective-C has no /// concept of [`Rc`]. Retain/release operations are always atomic. /// /// [`Arc`]: alloc::sync::Arc /// [`rc::Weak`]: crate::rc::Weak /// [`Rc`]: std::rc::Rc /// /// /// # Forwarding implementations /// /// Since `Retained<T>` is a smart pointer, it [`Deref`]s to `T`. /// /// It also forwards the implementation of a bunch of standard library traits /// such as [`PartialEq`], [`AsRef`], and so on, so that it becomes possible /// to use e.g. `Retained<NSString>` as if it was `NSString`. Note that having /// `NSString` directly is not possible since Objective-C objects cannot live /// on the stack, but instead must reside on the heap, and as such must be /// accessed behind a pointer or a reference (i.e. `&NSString`). /// /// Note that because of current limitations in the Rust trait system, some /// traits like [`Default`], [`IntoIterator`], [`FromIterator`], [`From`] and /// [`Into`] are not directly implementable on `NSString`; for that use-case, /// we instead provide the [`DefaultRetained`], [`RetainedIntoIterator`] and /// [`RetainedFromIterator`] traits, which make some of the the aforementioned /// traits implementable on `Retained`. /// /// [`DefaultRetained`]: crate::rc::DefaultRetained /// [`RetainedIntoIterator`]: crate::rc::RetainedIntoIterator /// [`RetainedFromIterator`]: crate::rc::RetainedFromIterator /// /// /// # Memory layout /// /// This is guaranteed to have the same size and alignment as a pointer to the /// object, `*const T`. /// /// Additionally, it participates in the null-pointer optimization, that is, /// `Option<Retained<T>>` is guaranteed to have the same size as /// `Retained<T>`. /// /// /// # Example /// /// Various usage of `Retained` on an immutable object. /// /// ``` /// # use objc2::runtime::NSObject; /// # #[cfg(available_in_foundation)] /// use objc2_foundation::{NSObject, NSString}; /// use objc2::rc::Retained; /// use objc2::{ClassType, msg_send}; /// # /// # objc2::extern_class!( /// # #[unsafe(super(NSObject))] /// # pub struct NSString; /// # ); /// /// // Use `msg_send!` to create an `Retained` with correct memory management /// // /// // SAFETY: The types are correct, and it is safe to call the `new` /// // selector on `NSString`. /// let string: Retained<NSString> = unsafe { msg_send![NSString::class(), new] }; /// // Or: /// // let string = NSString::new(); /// /// // Methods on `NSString` is usable via `Deref` /// # #[cfg(available_in_foundation)] /// assert_eq!(string.length(), 0); /// /// // Bump the reference count of the object. /// let another_ref: Retained<NSString> = string.clone(); /// /// // Convert one of the references to a reference to `NSObject` instead /// let obj: Retained<NSObject> = string.into_super(); /// /// // And use the `Debug` impl from that /// assert_eq!(format!("{obj:?}"), ""); /// /// // Finally, the `Retained`s go out of scope, the reference counts are /// // decreased, and the string will deallocate /// ``` #[repr(transparent)] #[doc(alias = "id")] #[doc(alias = "Id")] // Previous name #[doc(alias = "StrongPtr")] #[cfg_attr(
feature = "unstable-coerce-pointee",
derive(std::marker::CoercePointee)
)] // TODO: Add `ptr::Thin` bound on `T` to allow for only extern types pubstruct Retained<T: ?Sized> { /// A pointer to the contained object. The pointer is always retained. /// /// It is important that this is `NonNull`, since we want to dereference /// it later, and be able to use the null-pointer optimization. /// /// Additionally, covariance is correct because we're either the unique /// owner of `T`, or `T` is immutable.
ptr: NonNull<T>, /// Necessary for dropck even though we never actually run T's destructor, /// because it might have a `dealloc` that assumes that contained /// references outlive the type. /// /// See <https://doc.rust-lang.org/nightly/nomicon/phantom-data.html>
item: PhantomData<T>, /// Marks the type as !UnwindSafe. Later on we'll re-enable this. /// /// See <https://github.com/rust-lang/rust/issues/93367> for why this is /// required.
notunwindsafe: PhantomData<&'static mut ()>,
}
/// Short type-alias to [`Retained`]. /// /// This is fully deprecated since `v0.6.0`, use [`Retained`] instead. #[deprecated(since = "0.6.0", note = "Renamed to `Retained`.")] pubtype Id<T> = Retained<T>;
impl<T: ?Sized + Message> Retained<T> { /// Construct an [`Retained`] from a pointer that already has +1 retain count. /// /// Returns `None` if the pointer was NULL. /// /// This is useful when you have a retain count that has been handed off /// from somewhere else, usually Objective-C methods like `init`, `alloc`, /// `new`, `copy`, or methods with the `ns_returns_retained` attribute. /// /// If you do not have +1 retain count, such as if your object was /// retrieved from other methods than the ones noted above, use /// [`Retained::retain`] instead. /// /// /// # Safety /// /// You must uphold the same requirements as described in [`Retained::retain`]. /// /// Additionally, you must ensure the given object pointer has +1 retain /// count. /// /// /// # Example /// /// Comparing different ways of creating a new `NSObject`. /// /// ``` /// use objc2::rc::Retained; /// use objc2::runtime::NSObject; /// use objc2::{msg_send, AnyThread, ClassType}; /// /// // Manually using `msg_send!`, pointers and `Retained::from_raw` /// let obj: *mut NSObject = unsafe { msg_send![NSObject::class(), alloc] }; /// let obj: *mut NSObject = unsafe { msg_send![obj, init] }; /// // SAFETY: `-[NSObject init]` returns +1 retain count /// let obj: Retained<NSObject> = unsafe { Retained::from_raw(obj).unwrap() }; /// /// // Or automatically by specifying `Retained` as the return value from /// // `msg_send!` (it will do the correct conversion internally). /// let obj: Retained<NSObject> = unsafe { msg_send![NSObject::alloc(), init] }; /// /// // Or using the `NSObject::new` method /// let obj = NSObject::new(); /// ``` #[inline] // Note: We don't take a reference as a parameter since it would be too // easy to accidentally create two aliasing mutable references. pubunsafefn from_raw(ptr: *mut T) -> Option<Self> { // Should optimize down to a noop. // SAFETY: Upheld by the caller
NonNull::new(ptr).map(|ptr| unsafe { Retained::new_nonnull(ptr) })
}
/// Deprecated alias for [`Retained::from_raw`], see that for details. /// /// /// # Safety /// /// Same as [`Retained::from_raw`]. #[deprecated = "use the more descriptive name `Retained::from_raw` instead"] #[inline] pubunsafefn new(ptr: *mut T) -> Option<Self> { // SAFETY: Upheld by caller unsafe { Self::from_raw(ptr) }
}
/// Consumes the `Retained`, returning a raw pointer with +1 retain count. /// /// After calling this function, the caller is responsible for the memory /// previously managed by the `Retained`. /// /// This is effectively the opposite of [`Retained::from_raw`], see that for /// more details on when this function is useful. /// /// /// # Examples /// /// Converting an `Retained` to a pointer and back. /// /// ``` /// use objc2::rc::Retained; /// use objc2::runtime::NSObject; /// /// let obj = NSObject::new(); /// let ptr = Retained::into_raw(obj); /// // SAFETY: The pointer is valid, and has +1 retain count from above. /// let obj = unsafe { Retained::from_raw(ptr) }.unwrap(); /// ``` #[inline] pubfn into_raw(this: Self) -> *mut T {
ManuallyDrop::new(this).ptr.as_ptr()
}
/// Returns a raw pointer to the object. /// /// The pointer is valid for at least as long as the `Retained` is held. /// /// This is an associated method, and must be called as `Retained::as_ptr(obj)`. #[inline] pubfn as_ptr(this: &Self) -> *const T {
this.ptr.as_ptr()
}
#[inline] pub(crate) fn consume_as_ptr_option(this: Option<Self>) -> *mut T where
T: Sized,
{
this.map(|this| Retained::into_raw(this))
.unwrap_or_else(ptr::null_mut)
}
}
// TODO: Add ?Sized bound impl<T: Message> Retained<T> { /// Attempt to downcast the object to a class of type `U`. /// /// See [`AnyObject::downcast_ref`] for more details. /// /// # Errors /// /// If casting failed, this will return the object back as the [`Err`] /// type. If you do not care about this, and just want an [`Option`], use /// `.downcast().ok()`. /// /// # Example /// /// Cast a string to an object, and back again. /// /// ``` /// use objc2_foundation::{NSString, NSObject}; /// /// let string = NSString::new(); /// // The string is an object /// let obj = string.downcast::<NSObject>().unwrap(); /// // And it is also a string /// let string = obj.downcast::<NSString>().unwrap(); /// ``` /// /// Try to cast an object to a string, which will fail and return the /// object in [`Err`]. /// /// ``` /// use objc2_foundation::{NSString, NSObject}; /// /// let obj = NSObject::new(); /// let obj = obj.downcast::<NSString>().unwrap_err(); /// ``` // // NOTE: This is _not_ an associated method, since we want it to be easy // to call, and it does not conflict with `AnyObject::downcast_ref`. #[inline] pubfn downcast<U: DowncastTarget>(self) -> Result<Retained<U>, Retained<T>> where Self: 'static,
{ let ptr: *const AnyObject = Self::as_ptr(&self).cast(); // SAFETY: All objects are valid to re-interpret as `AnyObject`, even // if the object has a lifetime (which it does not in our case). let obj: &AnyObject = unsafe { &*ptr };
if obj.is_kind_of_class(U::class()).as_bool() { // SAFETY: Just checked that the object is a class of type `U`, // and `T` is `'static`. // // Generic `U` like `NSArray<NSString>` are ruled out by // `U: DowncastTarget`.
Ok(unsafe { Self::cast_unchecked::<U>(self) })
} else {
Err(self)
}
}
/// Convert the type of the given object to another. /// /// This is equivalent to a `cast` between two pointers. /// /// See [`Retained::into_super`], [`ProtocolObject::from_retained`] and /// [`Retained::downcast`] for safe alternatives. /// /// This is common to do when you know that an object is a subclass of /// a specific class (e.g. casting an instance of `NSString` to `NSObject` /// is safe because `NSString` is a subclass of `NSObject`), but do not /// want to pay the (very slight) performance price of dynamically /// checking that precondition with a [`downcast`]. /// /// All `'static` objects can safely be cast to [`AnyObject`], since that /// assumes no specific class. /// /// This is an associated method, and must be called as /// `Retained::cast_unchecked(obj)`. /// /// [`AnyObject`]: crate::runtime::AnyObject /// [`ProtocolObject::from_retained`]: crate::runtime::ProtocolObject::from_retained /// [`downcast`]: Self::downcast /// /// /// # Safety /// /// You must ensure that the object can be reinterpreted as the given /// type. /// /// If `T` is not `'static`, you must ensure that `U` ensures that the /// data contained by `T` is kept alive for as long as `U` lives. /// /// Additionally, you must ensure that any safety invariants that the new /// type has are upheld. #[inline] pubunsafefn cast_unchecked<U: Message>(this: Self) -> Retained<U> { let ptr = ManuallyDrop::new(this).ptr.cast(); // SAFETY: The object is forgotten, so we have +1 retain count. // // Caller verifies that the returned object is of the correct type. unsafe { Retained::new_nonnull(ptr) }
}
/// Deprecated alias of [`Retained::cast_unchecked`]. /// /// # Safety /// /// See [`Retained::cast_unchecked`]. #[inline] #[deprecated = "Use `downcast`, or `cast_unchecked` instead"] pubunsafefn cast<U: Message>(this: Self) -> Retained<U> { unsafe { Self::cast_unchecked(this) }
}
/// Retain the pointer and construct an [`Retained`] from it. /// /// Returns `None` if the pointer was NULL. /// /// This is useful when you have been given a pointer to an object from /// some API, and you would like to ensure that the object stays around /// while you work on it. /// /// For normal Objective-C methods, you may want to use /// [`Retained::retain_autoreleased`] instead, as that is usually more /// performant. /// /// See also [`Message::retain`] for a safe alternative where you already /// have a reference to the object. /// /// /// # Safety /// /// The pointer must be valid as a reference (aligned, dereferenceable and /// initialized, see the [`std::ptr`] module for more information), or /// NULL. /// /// You must ensure that any data that `T` may reference lives for at /// least as long as `T`. /// /// [`std::ptr`]: core::ptr #[doc(alias = "objc_retain")] #[inline] pubunsafefn retain(ptr: *mut T) -> Option<Retained<T>> { // SAFETY: The caller upholds that the pointer is valid let res: *mut T = unsafe { objc_retain_fast(ptr.cast()) }.cast();
debug_assert_eq!(res, ptr, "objc_retain did not return the same pointer"); // SAFETY: We just retained the object, so it has +1 retain count unsafe { Self::from_raw(res) }
}
// Only worth doing on the Apple runtime. // Not supported on TARGET_OS_WIN32. #[cfg(target_vendor = "apple")]
{ // Supported since macOS 10.7. #[cfg(target_arch = "x86_64")]
{ // x86_64 looks at the next call instruction. // // This is expected to be a PLT entry - if the user specifies // `-Zplt=no`, a GOT entry will be created instead, and this // will not work.
}
// Supported since macOS 10.10. // // On macOS 13.0 / iOS 16.0 / tvOS 16.0 / watchOS 9.0, the runtime // instead checks the return pointer address, so we no longer need // to emit these extra instructions, see this video from WWDC22: // https://developer.apple.com/videos/play/wwdc2022/110363/ #[cfg(all(target_arch = "aarch64", not(feature = "unstable-apple-new")))] unsafe { // Same as `mov x29, x29`.
core::arch::asm!("mov fp, fp", options(nomem, preserves_flags, nostack))
};
// SAFETY: Same as `Retained::retain`, this is just an optimization. let res: *mut T = unsafe { ffi::objc_retainAutoreleasedReturnValue(ptr.cast()) }.cast();
// Ideally, we'd be able to specify that the above call should never // be tail-call optimized (become a `jmp` instruction instead of a // `call`); Rust doesn't really have a way of doing this currently, so // we emit a `nop` to make such tail-call optimizations less likely to // occur. // // This is brittle! We should find a better solution! #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))]
{ // SAFETY: Similar to above. unsafe { core::arch::asm!("nop", options(nomem, preserves_flags, nostack)) }; // TODO: Possibly more efficient alternative? Also consider PLT. // #![feature(asm_sym)] // core::arch::asm!( // "mov rdi, rax", // "call {}", // sym objc2::ffi::objc_retainAutoreleasedReturnValue, // inout("rax") obj, // clobber_abi("C-unwind"), // );
}
debug_assert_eq!(
res, ptr, "objc_retainAutoreleasedReturnValue did not return the same pointer"
);
// SAFETY: Same as `Retained::retain`. unsafe { Self::from_raw(res) }
}
/// Autoreleases the [`Retained`], returning a pointer. /// /// The object is not immediately released, but will be when the innermost /// / current autorelease pool is drained. /// /// This is useful when defining your own classes and you have some error /// parameter passed as `Option<&mut *mut NSError>`, and you want to /// create and autorelease an error before returning. /// /// This is an associated method, and must be called as /// `Retained::autorelease_ptr(obj)`. /// /// # Safety /// /// This method is safe to call, but the returned pointer is only /// guaranteed to be valid until the innermost autorelease pool is /// drained. #[doc(alias = "objc_autorelease")] #[must_use = "if you don't intend to use the object any more, drop it as usual"] #[inline] pubfn autorelease_ptr(this: Self) -> *mut T { let ptr = ManuallyDrop::new(this).ptr.as_ptr(); // SAFETY: // - The `ptr` is guaranteed to be valid and have at least one // retain count. // - Because of the ManuallyDrop, we don't call the Drop // implementation, so the object won't also be released there. let res: *mut T = unsafe { ffi::objc_autorelease(ptr.cast()) }.cast();
debug_assert_eq!(res, ptr, "objc_autorelease did not return the same pointer");
res
}
/// Autoreleases the [`Retained`], returning a reference bound to the pool. /// /// The object is not immediately released, but will be when the innermost /// / current autorelease pool (given as a parameter) is drained. /// /// This is an associated method, and must be called as /// `Retained::autorelease(obj, pool)`. /// /// # Safety /// /// The given pool must represent the innermost pool, to ensure that the /// reference is not moved outside the autorelease pool into which it has /// been put in. #[doc(alias = "objc_autorelease")] #[must_use = "if you don't intend to use the object any more, drop it as usual"] #[inline] #[allow(clippy::needless_lifetimes)] pubunsafefn autorelease<'p>(this: Self, pool: AutoreleasePool<'p>) -> &'p T { let ptr = Self::autorelease_ptr(this); // SAFETY: The pointer is valid as a reference unsafe { pool.ptr_as_ref(ptr) }
}
#[inline] pub(crate) fn autorelease_return_option(this: Option<Self>) -> *mut T { let ptr: *mut T = this
.map(|this| ManuallyDrop::new(this).ptr.as_ptr())
.unwrap_or_else(ptr::null_mut);
// SAFETY: Same as `autorelease_inner`, this is just an optimization. let res: *mut T = unsafe { ffi::objc_autoreleaseReturnValue(ptr.cast()) }.cast();
debug_assert_eq!(
res, ptr, "objc_autoreleaseReturnValue did not return the same pointer"
);
res
}
/// Autoreleases and prepares the [`Retained`] to be returned to Objective-C. /// /// The object is not immediately released, but will be when the innermost /// autorelease pool is drained. /// /// This is useful when [defining your own methods][classbuilder] where /// you will often find yourself in need of returning autoreleased objects /// to properly follow [Cocoa's Memory Management Policy][mmRules]. /// /// To that end, you could also use [`Retained::autorelease_ptr`], but /// this is more efficient than a normal `autorelease`, since it makes a /// best effort attempt to hand off ownership of the retain count to a /// subsequent call to `objc_retainAutoreleasedReturnValue` / /// [`Retained::retain_autoreleased`] in the enclosing call frame. /// /// This optimization relies heavily on this function being tail called, /// so make sure you only call this function at the end of your method. /// /// [classbuilder]: crate::runtime::ClassBuilder /// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html /// /// /// # Example /// /// Returning an `Retained` from a custom method (note: the /// [`define_class!`] macro supports doing this for you automatically). /// /// ``` /// use objc2::{class, msg_send, sel}; /// use objc2::rc::Retained; /// use objc2::runtime::{AnyClass, AnyObject, ClassBuilder, Sel}; /// /// let mut builder = ClassBuilder::new(c"ExampleObject", class!(NSObject)).unwrap(); /// /// extern "C-unwind" fn get(cls: &AnyClass, _cmd: Sel) -> *mut AnyObject { /// let obj: Retained<AnyObject> = unsafe { msg_send![cls, new] }; /// Retained::autorelease_return(obj) /// } /// /// unsafe { /// builder.add_class_method( /// sel!(get), /// get as extern "C-unwind" fn(_, _) -> _, /// ); /// } /// /// let cls = builder.register(); /// ``` /// /// [`define_class!`]: crate::define_class #[doc(alias = "objc_autoreleaseReturnValue")] #[must_use = "if you don't intend to use the object any more, drop it as usual"] #[inline] pubfn autorelease_return(this: Self) -> *mut T { Self::autorelease_return_option(Some(this))
}
}
impl<T: ClassType + 'static> Retained<T> where
T::Super: 'static,
{ /// Convert the object into its superclass. // // NOTE: This is _not_ an associated method, since we want it to be easy // to call, and it it unlikely to conflict with anything (the reference // version is called `ClassType::as_super`). #[inline] pubfn into_super(self) -> Retained<T::Super> { // SAFETY: // - The casted-to type is a superclass of the type. // - Both types are `'static`, so no lifetime information is lost // (this could maybe be relaxed a bit, but let's be on the safe side // for now). unsafe { Self::cast_unchecked::<T::Super>(self) }
}
}
// TODO: Add ?Sized bound impl<T: Message> Clone for Retained<T> { /// Retain the object, increasing its reference count. /// /// This is equivalent to [`Message::retain`]. #[doc(alias = "objc_retain")] #[doc(alias = "retain")] #[inline] fn clone(&self) -> Self { self.retain()
}
}
/// `#[may_dangle]` (see [this][dropck_eyepatch]) doesn't apply here since we /// don't run `T`'s destructor (rather, we want to discourage having `T`s with /// a destructor); and even if we did run the destructor, it would not be safe /// to add since we cannot verify that a `dealloc` method doesn't access /// borrowed data. /// /// [dropck_eyepatch]: https://doc.rust-lang.org/nightly/nomicon/dropck.html#an-escape-hatch impl<T: ?Sized> Drop for Retained<T> { /// Releases the retained object. /// /// The contained object's destructor (`Drop` impl, if it has one) is /// never run - override the `dealloc` method instead (which /// `define_class!` does for you). #[doc(alias = "objc_release")] #[doc(alias = "release")] #[inline] fn drop(&mutself) { // We could technically run the destructor for `T` when it is mutable, // but that would be confusing and inconsistent since we cannot really // guarantee that it is run if the `Retained<T>` is passed to Objective-C.
// SAFETY: The `ptr` is guaranteed to be valid and have at least one // retain count. unsafe { objc_release_fast(self.ptr.as_ptr().cast()) };
}
}
impl<T: ?Sized> Deref for Retained<T> { type Target = T;
/// Obtain an immutable reference to the object. // Box doesn't inline, but that's because it's a compiler built-in #[inline] fn deref(&self) -> &T { // SAFETY: The pointer's validity is verified when the type is // created. unsafe { self.ptr.as_ref() }
}
}
// Sadly, it is not possible to implement general conversions between // `Retained`, as it conflicts with the generic `impl From<T> for T`. // // impl<T: Upcast, U> From<Retained<U>> for Retained<T> { // fn from(obj: &Retained<T>) -> Self { // obj.as_super().retain() // } // } // // But we _can_ do the following implementations:
impl<T: ?Sized + AsRef<U>, U: Message> From<&T> for Retained<U> { /// Cast the object to its superclass, and retain it. #[inline] fn from(obj: &T) -> Self {
obj.as_ref().retain()
}
}
// Bounded by `T: ClassType` to prevent overlapping impls // (`AnyObject` implements `Message`). impl<T: ClassType + 'static> From<Retained<T>> for Retained<AnyObject> { /// Convert the object to `AnyObject`. #[inline] fn from(obj: Retained<T>) -> Self { // SAFETY: All 'static objects can be converted to `AnyObject`. unsafe { Retained::cast_unchecked(obj) }
}
}
impl<P: ?Sized + 'static> From<Retained<ProtocolObject<P>>> for Retained<AnyObject> { /// Convert the protocol object to `AnyObject`. #[inline] fn from(obj: Retained<ProtocolObject<P>>) -> Self { // SAFETY: All protocol objects are Objective-C objects too. unsafe { Retained::cast_unchecked(obj) }
}
}
/// `Retained<T>` is `Send` if `T` is `Send + Sync`. // // SAFETY: // - `T: Send` is required because otherwise you could move the object to // another thread and let `dealloc` get called there. // - `T: Sync` is required because otherwise you could clone `&Retained<T>`, // send it to another thread, and drop the clone last, making `dealloc` get // called on the other thread. // // This is the same reasoning as for `Arc`. // https://doc.rust-lang.org/nomicon/arc-mutex/arc-base.html#send-and-sync unsafeimpl<T: ?Sized + Sync + Send> Send for Retained<T> {}
/// `Retained<T>` is `Sync` if `T` is `Send + Sync`. // // SAFETY: // - `T: Sync` is required because `&Retained<T>` give access to `&T`. // -`T: Send` is required because otherwise you could clone `&Retained<T>` // from another thread, and drop the clone last, making `dealloc` get called // on the other thread. // // This is the same reasoning as for `Arc`. // https://doc.rust-lang.org/nomicon/arc-mutex/arc-base.html#send-and-sync unsafeimpl<T: ?Sized + Sync + Send> Sync for Retained<T> {}
/// Test that `Retained<T>` is covariant over `T`. #[allow(unused)] fn assert_retained_variance<'b>(obj: Retained<MyObject<'static>>) -> Retained<MyObject<'b>> {
obj
}
#[test] fn test_size_of() { let ptr_size = size_of::<&NSObject>();
#[test] fn test_into() { let obj = NSObject::new(); let obj: Retained<NSObject> = Into::into(obj); let _: Retained<AnyObject> = Into::into(obj);
let obj_ref = &*NSObject::new(); let _: Retained<NSObject> = Into::into(obj_ref); let _: Retained<AnyObject> = Into::into(obj_ref);
let obj_retained_ref = &NSObject::new(); let _: Retained<NSObject> = Into::into(obj_retained_ref); let _: Retained<AnyObject> = Into::into(obj_retained_ref);
let protocol_obj = ProtocolObject::<dyn NSObjectProtocol>::from_retained(NSObject::new()); let _: Retained<AnyObject> = Into::into(protocol_obj);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.