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::{Id, Shared};
use crate::ffi;
use crate::Message;
#[repr(transparent)]
pub struct WeakId<T: ?Sized> {
inner: Box<UnsafeCell<*mut ffi::objc_object>>,
item: PhantomData<Id<T, Shared>>,
}
impl<T: Message> WeakId<T> {
#[doc(alias = "objc_initWeak")]
#[inline]
pub fn new(obj: &Id<T, Shared>) -> Self {
unsafe { Self::new_inner(Id::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 as *mut ffi::objc_object) };
Self {
inner,
item: PhantomData,
}
}
#[doc(alias = "upgrade")]
#[doc(alias = "retain")]
#[doc(alias = "objc_loadWeak")]
#[doc(alias = "objc_loadWeakRetained")]
#[inline]
pub fn load(&self) -> Option<Id<T, Shared>> {
let ptr = self.inner.get();
let obj = unsafe { ffi::objc_loadWeakRetained(ptr) } as *mut T;
unsafe { Id::new(obj) }
}
}
impl<T: ?Sized> Drop for WeakId<T> {
#[doc(alias = "objc_destroyWeak")]
#[inline]
fn drop(&mut self) {
unsafe { ffi::objc_destroyWeak(self.inner.get()) }
}
}
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(), self.inner.get()) };
Self {
inner: ptr,
item: PhantomData,
}
}
}
impl<T: Message> Default for WeakId<T> {
#[inline]
fn default() -> Self {
unsafe { Self::new_inner(ptr::null()) }
}
}
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> {}
impl<T: Message> From<Id<T, Shared>> for WeakId<T> {
#[inline]
fn from(obj: Id<T, Shared>) -> Self {
WeakId::new(&obj)
}
}
impl<T: Message> TryFrom<WeakId<T>> for Id<T, Shared> {
type Error = ();
fn try_from(weak: WeakId<T>) -> Result<Self, ()> {
weak.load().ok_or(())
}
}
#[cfg(test)]
mod tests {
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(obj).unwrap()
};
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]).unwrap() };
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);
}
}