use alloc::boxed::Box;
use core::cell::UnsafeCell;
use core::fmt;
use core::marker::PhantomData;
use core::ptr;
use std::panic::{RefUnwindSafe, UnwindSafe};
use super::Retained;
use crate::runtime::AnyObject;
use crate::{ffi, Message};
#[repr(transparent)] #[doc(alias = "WeakId")] pub struct Weak<T: ?Sized> {
inner: Box<UnsafeCell<*mut AnyObject>>,
item: PhantomData<Retained<T>>,
}
#[deprecated(since = "0.6.0", note = "Renamed to `Weak`.")]
pub type WeakId<T> = Weak<T>;
impl<T: Message> Weak<T> {
#[doc(alias = "objc_initWeak")]
#[inline]
pub fn new(obj: &T) -> Self {
unsafe { Self::new_inner(obj) }
}
#[doc(alias = "objc_initWeak")]
#[deprecated = "use `Weak::from_retained` instead"]
#[inline]
pub fn from_id(obj: &Retained<T>) -> Self {
Self::from_retained(obj)
}
#[doc(alias = "objc_initWeak")]
#[inline]
pub fn from_retained(obj: &Retained<T>) -> Self {
unsafe { Self::new_inner(Retained::as_ptr(obj)) }
}
unsafe fn new_inner(obj: *const T) -> Self {
let inner = Box::new(UnsafeCell::new(ptr::null_mut()));
let _ = unsafe { ffi::objc_initWeak(inner.get(), (obj as *mut T).cast()) };
Self {
inner,
item: PhantomData,
}
}
#[doc(alias = "retain")]
#[doc(alias = "objc_loadWeak")]
#[doc(alias = "objc_loadWeakRetained")]
#[inline]
pub fn load(&self) -> Option<Retained<T>> {
let ptr = self.inner.get();
let obj = unsafe { ffi::objc_loadWeakRetained(ptr) }.cast();
unsafe { Retained::from_raw(obj) }
}
}
impl<T: ?Sized> Drop for Weak<T> {
#[doc(alias = "objc_destroyWeak")]
#[inline]
fn drop(&mut self) {
unsafe { ffi::objc_destroyWeak(self.inner.get()) }
}
}
impl<T: Message> Clone for Weak<T> {
#[doc(alias = "objc_copyWeak")]
fn clone(&self) -> Self {
let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
unsafe { ffi::objc_copyWeak(ptr.get(), self.inner.get()) };
Self {
inner: ptr,
item: PhantomData,
}
}
}
impl<T: Message> Default for Weak<T> {
#[inline]
fn default() -> Self {
unsafe { Self::new_inner(ptr::null()) }
}
}
impl<T: ?Sized> fmt::Debug for Weak<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(Weak)")
}
}
unsafe impl<T: ?Sized + Sync + Send> Sync for Weak<T> {}
unsafe impl<T: ?Sized + Sync + Send> Send for Weak<T> {}
impl<T: ?Sized> Unpin for Weak<T> {}
impl<T: ?Sized + RefUnwindSafe> RefUnwindSafe for Weak<T> {}
impl<T: ?Sized + RefUnwindSafe> UnwindSafe for Weak<T> {}
impl<T: Message> From<&T> for Weak<T> {
#[inline]
fn from(obj: &T) -> Self {
Weak::new(obj)
}
}
impl<T: Message> From<&Retained<T>> for Weak<T> {
#[inline]
fn from(obj: &Retained<T>) -> Self {
Weak::from_retained(obj)
}
}
impl<T: Message> From<Retained<T>> for Weak<T> {
#[inline]
fn from(obj: Retained<T>) -> Self {
Weak::from_retained(&obj)
}
}
#[cfg(test)]
mod tests {
use core::mem;
use super::*;
use crate::rc::{RcTestObject, ThreadTestData};
use crate::runtime::NSObject;
#[test]
fn test_weak() {
let obj = RcTestObject::new();
let mut expected = ThreadTestData::current();
let weak = Weak::from(&obj);
expected.assert_current();
let strong = weak.load().unwrap();
expected.try_retain += 1;
expected.assert_current();
assert!(ptr::eq(&*strong, &*obj));
drop(obj);
drop(strong);
expected.release += 2;
expected.drop += 1;
expected.assert_current();
if cfg!(not(feature = "gnustep-1-7")) {
assert!(weak.load().is_none());
expected.assert_current();
}
drop(weak);
expected.assert_current();
}
#[test]
fn test_weak_clone() {
let obj = RcTestObject::new();
let mut expected = ThreadTestData::current();
let weak = Weak::from(&obj);
expected.assert_current();
let weak2 = weak.clone();
if cfg!(target_vendor = "apple") {
expected.try_retain += 1;
expected.release += 1;
}
expected.assert_current();
let strong = weak.load().unwrap();
expected.try_retain += 1;
expected.assert_current();
assert!(ptr::eq(&*strong, &*obj));
let strong2 = weak2.load().unwrap();
expected.try_retain += 1;
expected.assert_current();
assert!(ptr::eq(&*strong, &*strong2));
drop(weak);
drop(weak2);
expected.assert_current();
}
#[test]
fn test_weak_default() {
let weak: Weak<RcTestObject> = Weak::default();
assert!(weak.load().is_none());
drop(weak);
}
#[repr(C)]
struct MyObject<'a> {
inner: NSObject,
p: PhantomData<&'a str>,
}
#[allow(unused)]
fn assert_variance<'a, 'b>(obj: &'a Weak<MyObject<'static>>) -> &'a Weak<MyObject<'b>> {
obj
}
#[test]
fn test_size_of() {
assert_eq!(
mem::size_of::<Option<Weak<NSObject>>>(),
mem::size_of::<*const ()>()
);
}
}