1use alloc::boxed::Box;
2use core::cell::UnsafeCell;
3use core::fmt;
4use core::marker::PhantomData;
5use core::ptr;
6use std::panic::{RefUnwindSafe, UnwindSafe};
7
8use super::Retained;
9use crate::runtime::AnyObject;
10use crate::{ffi, Message};
11
12#[repr(transparent)] #[doc(alias = "WeakId")] pub struct Weak<T: ?Sized> {
33 inner: Box<UnsafeCell<*mut AnyObject>>,
47 item: PhantomData<Retained<T>>,
50}
51
52#[deprecated(since = "0.6.0", note = "Renamed to `Weak`.")]
54pub type WeakId<T> = Weak<T>;
55
56impl<T: Message> Weak<T> {
57 #[doc(alias = "objc_initWeak")]
59 #[inline]
60 pub fn new(obj: &T) -> Self {
61 unsafe { Self::new_inner(obj) }
63 }
64
65 #[doc(alias = "objc_initWeak")]
67 #[deprecated = "use `Weak::from_retained` instead"]
68 #[inline]
69 pub fn from_id(obj: &Retained<T>) -> Self {
70 Self::from_retained(obj)
71 }
72
73 #[doc(alias = "objc_initWeak")]
75 #[inline]
76 pub fn from_retained(obj: &Retained<T>) -> Self {
77 unsafe { Self::new_inner(Retained::as_ptr(obj)) }
79 }
80
81 unsafe fn new_inner(obj: *const T) -> Self {
88 let inner = Box::new(UnsafeCell::new(ptr::null_mut()));
89 let _ = unsafe { ffi::objc_initWeak(inner.get(), (obj as *mut T).cast()) };
91 Self {
92 inner,
93 item: PhantomData,
94 }
95 }
96
97 #[doc(alias = "retain")]
102 #[doc(alias = "objc_loadWeak")]
103 #[doc(alias = "objc_loadWeakRetained")]
104 #[inline]
105 pub fn load(&self) -> Option<Retained<T>> {
106 let ptr = self.inner.get();
107 let obj = unsafe { ffi::objc_loadWeakRetained(ptr) }.cast();
108 unsafe { Retained::from_raw(obj) }
110 }
111
112 }
114
115impl<T: ?Sized> Drop for Weak<T> {
116 #[doc(alias = "objc_destroyWeak")]
118 #[inline]
119 fn drop(&mut self) {
120 unsafe { ffi::objc_destroyWeak(self.inner.get()) }
121 }
122}
123
124impl<T: Message> Clone for Weak<T> {
126 #[doc(alias = "objc_copyWeak")]
128 fn clone(&self) -> Self {
129 let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
130 unsafe { ffi::objc_copyWeak(ptr.get(), self.inner.get()) };
131 Self {
132 inner: ptr,
133 item: PhantomData,
134 }
135 }
136}
137
138impl<T: Message> Default for Weak<T> {
140 #[inline]
144 fn default() -> Self {
145 unsafe { Self::new_inner(ptr::null()) }
147 }
148}
149
150impl<T: ?Sized> fmt::Debug for Weak<T> {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 write!(f, "(Weak)")
156 }
157}
158
159unsafe impl<T: ?Sized + Sync + Send> Sync for Weak<T> {}
161
162unsafe impl<T: ?Sized + Sync + Send> Send for Weak<T> {}
164
165impl<T: ?Sized> Unpin for Weak<T> {}
167
168impl<T: ?Sized + RefUnwindSafe> RefUnwindSafe for Weak<T> {}
170
171impl<T: ?Sized + RefUnwindSafe> UnwindSafe for Weak<T> {}
173
174impl<T: Message> From<&T> for Weak<T> {
175 #[inline]
176 fn from(obj: &T) -> Self {
177 Weak::new(obj)
178 }
179}
180
181impl<T: Message> From<&Retained<T>> for Weak<T> {
182 #[inline]
183 fn from(obj: &Retained<T>) -> Self {
184 Weak::from_retained(obj)
185 }
186}
187
188impl<T: Message> From<Retained<T>> for Weak<T> {
189 #[inline]
190 fn from(obj: Retained<T>) -> Self {
191 Weak::from_retained(&obj)
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use core::mem;
198
199 use super::*;
200 use crate::rc::{RcTestObject, ThreadTestData};
201 use crate::runtime::NSObject;
202
203 #[test]
204 fn test_weak() {
205 let obj = RcTestObject::new();
206 let mut expected = ThreadTestData::current();
207
208 let weak = Weak::from(&obj);
209 expected.assert_current();
210
211 let strong = weak.load().unwrap();
212 expected.try_retain += 1;
213 expected.assert_current();
214 assert!(ptr::eq(&*strong, &*obj));
215
216 drop(obj);
217 drop(strong);
218 expected.release += 2;
219 expected.drop += 1;
220 expected.assert_current();
221
222 if cfg!(not(feature = "gnustep-1-7")) {
223 assert!(weak.load().is_none());
225 expected.assert_current();
226 }
227
228 drop(weak);
229 expected.assert_current();
230 }
231
232 #[test]
233 fn test_weak_clone() {
234 let obj = RcTestObject::new();
235 let mut expected = ThreadTestData::current();
236
237 let weak = Weak::from(&obj);
238 expected.assert_current();
239
240 let weak2 = weak.clone();
241 if cfg!(target_vendor = "apple") {
242 expected.try_retain += 1;
243 expected.release += 1;
244 }
245 expected.assert_current();
246
247 let strong = weak.load().unwrap();
248 expected.try_retain += 1;
249 expected.assert_current();
250 assert!(ptr::eq(&*strong, &*obj));
251
252 let strong2 = weak2.load().unwrap();
253 expected.try_retain += 1;
254 expected.assert_current();
255 assert!(ptr::eq(&*strong, &*strong2));
256
257 drop(weak);
258 drop(weak2);
259 expected.assert_current();
260 }
261
262 #[test]
263 fn test_weak_default() {
264 let weak: Weak<RcTestObject> = Weak::default();
265 assert!(weak.load().is_none());
266 drop(weak);
267 }
268
269 #[repr(C)]
270 struct MyObject<'a> {
271 inner: NSObject,
272 p: PhantomData<&'a str>,
273 }
274
275 #[allow(unused)]
277 fn assert_variance<'a, 'b>(obj: &'a Weak<MyObject<'static>>) -> &'a Weak<MyObject<'b>> {
278 obj
279 }
280
281 #[test]
282 fn test_size_of() {
283 assert_eq!(
284 mem::size_of::<Option<Weak<NSObject>>>(),
285 mem::size_of::<*const ()>()
286 );
287 }
288}