use alloc::boxed::Box;
use core::cell::UnsafeCell;
use core::fmt;
use core::marker::PhantomData;
use core::ptr;
use core::ptr::NonNull;
use std::panic::{RefUnwindSafe, UnwindSafe};
use super::{Id, Shared};
use crate::ffi;
use crate::Message;
#[repr(transparent)]
pub struct WeakId<T: ?Sized> {
inner: Box<UnsafeCell<*mut T>>,
item: PhantomData<Id<T, Shared>>,
}
impl<T: Message> WeakId<T> {
#[doc(alias = "objc_initWeak")]
pub fn new(obj: &Id<T, Shared>) -> Self {
unsafe { Self::new_inner(&**obj as *const T as *mut T) }
}
unsafe fn new_inner(obj: *mut T) -> Self {
let inner = Box::new(UnsafeCell::new(ptr::null_mut()));
let _ = unsafe { ffi::objc_initWeak(inner.get() as _, obj as _) };
Self {
inner,
item: PhantomData,
}
}
#[doc(alias = "upgrade")]
#[doc(alias = "objc_loadWeak")]
#[doc(alias = "objc_loadWeakRetained")]
#[inline]
pub fn load(&self) -> Option<Id<T, Shared>> {
let ptr: *mut *mut ffi::objc_object = self.inner.get() as _;
let obj = unsafe { ffi::objc_loadWeakRetained(ptr) } as *mut T;
NonNull::new(obj).map(|obj| unsafe { Id::new(obj) })
}
}
impl<T: ?Sized> Drop for WeakId<T> {
#[doc(alias = "objc_destroyWeak")]
fn drop(&mut self) {
unsafe { ffi::objc_destroyWeak(self.inner.get() as _) }
}
}
impl<T> Clone for WeakId<T> {
#[doc(alias = "objc_copyWeak")]
fn clone(&self) -> Self {
let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
unsafe { ffi::objc_copyWeak(ptr.get() as _, self.inner.get() as _) };
Self {
inner: ptr,
item: PhantomData,
}
}
}
impl<T: Message> Default for WeakId<T> {
fn default() -> Self {
unsafe { Self::new_inner(ptr::null_mut()) }
}
}
unsafe impl<T: Sync + Send + ?Sized> Sync for WeakId<T> {}
unsafe impl<T: Sync + Send + ?Sized> Send for WeakId<T> {}
impl<T: fmt::Debug + ?Sized> fmt::Debug for WeakId<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(WeakId)")
}
}
impl<T: ?Sized> Unpin for WeakId<T> {}
impl<T: RefUnwindSafe + ?Sized> RefUnwindSafe for WeakId<T> {}
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for WeakId<T> {}
#[cfg(test)]
mod tests {
use core::ptr::NonNull;
use super::WeakId;
use super::{Id, Shared};
use crate::runtime::Object;
use crate::{class, msg_send};
#[test]
fn test_weak() {
let cls = class!(NSObject);
let obj: Id<Object, Shared> = unsafe {
let obj: *mut Object = msg_send![cls, alloc];
let obj: *mut Object = msg_send![obj, init];
Id::new(NonNull::new_unchecked(obj))
};
let weak = WeakId::new(&obj);
let strong = weak.load().unwrap();
let strong_ptr: *const Object = &*strong;
let obj_ptr: *const Object = &*obj;
assert_eq!(strong_ptr, obj_ptr);
drop(strong);
drop(obj);
assert!(weak.load().is_none());
}
#[test]
fn test_weak_clone() {
let obj: Id<Object, Shared> = unsafe { Id::new(msg_send![class!(NSObject), new]) };
let weak = WeakId::new(&obj);
let weak2 = weak.clone();
let strong = weak.load().unwrap();
let strong2 = weak2.load().unwrap();
let strong_ptr: *const Object = &*strong;
let strong2_ptr: *const Object = &*strong2;
let obj_ptr: *const Object = &*obj;
assert_eq!(strong_ptr, obj_ptr);
assert_eq!(strong2_ptr, obj_ptr);
}
#[test]
fn test_weak_default() {
let weak: WeakId<Object> = WeakId::default();
assert!(weak.load().is_none());
drop(weak);
}
}