Skip to content

Commit 47e4749

Browse files
committed
Construct WeakId manually
Because I want to get rid of objc2::rc::WeakPtr. WeakId can now also use the null-pointer optimization, and it has a Default implementation.
1 parent 3183e75 commit 47e4749

File tree

3 files changed

+104
-22
lines changed

3 files changed

+104
-22
lines changed

objc2_id/Cargo.toml

-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,3 @@ license = "MIT"
1919
[dependencies]
2020
objc2 = { path = "../objc2" }
2121
objc2_sys = { path = "../objc2_sys" }
22-
23-
[features]
24-
default = [] # TODO: Add alloc?
25-
alloc = []

objc2_id/src/id.rs

+104-17
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
#[cfg(feature = "alloc")]
21
use alloc::borrow;
2+
use alloc::boxed::Box;
33
use core::any::Any;
4+
use core::cell::UnsafeCell;
45
use core::fmt;
56
use core::hash;
67
use core::iter::FusedIterator;
78
use core::marker::PhantomData;
89
use core::mem::ManuallyDrop;
910
use core::ops::{Deref, DerefMut};
11+
use core::ptr;
1012
use core::ptr::NonNull;
1113

1214
use objc2::rc::AutoreleasePool;
13-
use objc2::rc::WeakPtr;
14-
use objc2::runtime::Object;
1515
use objc2::Message;
1616

1717
/// A type used to mark that a struct owns the object(s) it contains,
@@ -60,6 +60,9 @@ impl Ownership for Shared {}
6060
/// a reference-counting pointer that, when cloned, increases the reference
6161
/// count.
6262
///
63+
/// [`Box`]: alloc::boxed::Box
64+
/// [`Arc`]: alloc::sync::Arc
65+
///
6366
/// # Caveats
6467
///
6568
/// If the inner type implements [`Drop`], that implementation will not be
@@ -179,6 +182,8 @@ impl<T: Message, O: Ownership> Id<T, O> {
179182
/// Additionally, the pointer must be valid as a reference (aligned,
180183
/// dereferencable and initialized, see the [`std::ptr`] module for more
181184
/// information).
185+
///
186+
/// [`std::ptr`]: core::ptr
182187
//
183188
// This would be illegal:
184189
// ```no_run
@@ -456,14 +461,12 @@ impl<I: ExactSizeIterator> ExactSizeIterator for Id<I, Owned> {
456461

457462
impl<I: FusedIterator> FusedIterator for Id<I, Owned> {}
458463

459-
#[cfg(feature = "alloc")]
460464
impl<T, O> borrow::Borrow<T> for Id<T, O> {
461465
fn borrow(&self) -> &T {
462466
&**self
463467
}
464468
}
465469

466-
#[cfg(feature = "alloc")]
467470
impl<T> borrow::BorrowMut<T> for Id<T, Owned> {
468471
fn borrow_mut(&mut self) -> &mut T {
469472
&mut **self
@@ -502,29 +505,91 @@ pub type ShareId<T> = Id<T, Shared>;
502505

503506
/// A pointer type for a weak reference to an Objective-C reference counted
504507
/// object.
508+
///
509+
/// Allows breaking reference cycles and safely checking whether the object
510+
/// has been deallocated.
511+
#[repr(transparent)]
505512
pub struct WeakId<T> {
506-
ptr: WeakPtr,
513+
/// We give the runtime the address to this box, so that it can modify it
514+
/// even if the `WeakId` is moved.
515+
///
516+
/// Loading may modify the pointer through a shared reference, so we use
517+
/// an UnsafeCell to get a *mut without self being mutable.
518+
inner: Box<UnsafeCell<*mut T>>,
519+
/// TODO: Variance and dropck
507520
item: PhantomData<T>,
508521
}
509522

510523
impl<T: Message> WeakId<T> {
511-
/// Construct a new [`WeakId`] referencing the given [`ShareId`].
512-
pub fn new(obj: &ShareId<T>) -> WeakId<T> {
513-
// SAFETY: The pointer is valid
514-
let ptr = unsafe { WeakPtr::new(obj.ptr.as_ptr() as *mut Object) };
515-
WeakId {
516-
ptr,
524+
/// Construct a new [`WeakId`] referencing the given shared [`Id`].
525+
#[doc(alias = "objc_initWeak")]
526+
pub fn new(obj: &Id<T, Shared>) -> Self {
527+
// Note that taking `&Id<T, Owned>` would not be safe since that would
528+
// allow loading an `Id<T, Shared>` later on.
529+
530+
// SAFETY: `obj` is valid
531+
unsafe { Self::new_inner(&**obj as *const T as *mut T) }
532+
}
533+
534+
/// # Safety
535+
///
536+
/// The object must be valid or null.
537+
unsafe fn new_inner(obj: *mut T) -> Self {
538+
let inner = Box::new(UnsafeCell::new(ptr::null_mut()));
539+
// SAFETY: `ptr` will never move, and the caller verifies `obj`
540+
objc2_sys::objc_initWeak(inner.get() as _, obj as _);
541+
Self {
542+
inner,
517543
item: PhantomData,
518544
}
519545
}
520546

521-
/// Load a [`ShareId`] from the [`WeakId`] if the object still exists.
547+
/// Load a shared (and retained) [`Id`] if the object still exists.
522548
///
523549
/// Returns [`None`] if the object has been deallocated.
524-
pub fn load(&self) -> Option<ShareId<T>> {
525-
let obj = self.ptr.load();
526-
let ptr = *ManuallyDrop::new(obj).deref().deref() as *mut T;
527-
NonNull::new(ptr).map(|ptr| unsafe { Id::new(ptr) })
550+
#[doc(alias = "upgrade")]
551+
#[doc(alias = "objc_loadWeak")]
552+
#[doc(alias = "objc_loadWeakRetained")]
553+
#[inline]
554+
pub fn load(&self) -> Option<Id<T, Shared>> {
555+
let ptr: *mut *mut objc2_sys::objc_object = self.inner.get() as _;
556+
let obj = unsafe { objc2_sys::objc_loadWeakRetained(ptr) } as *mut T;
557+
NonNull::new(obj).map(|obj| unsafe { Id::new(obj) })
558+
}
559+
}
560+
561+
impl<T> Drop for WeakId<T> {
562+
/// Drops the `WeakId` pointer.
563+
#[doc(alias = "objc_destroyWeak")]
564+
fn drop(&mut self) {
565+
unsafe {
566+
objc2_sys::objc_destroyWeak(self.inner.get() as _);
567+
}
568+
}
569+
}
570+
571+
impl<T> Clone for WeakId<T> {
572+
/// Makes a clone of the `WeakId` that points to the same object.
573+
#[doc(alias = "objc_copyWeak")]
574+
fn clone(&self) -> Self {
575+
let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
576+
unsafe {
577+
objc2_sys::objc_copyWeak(ptr.get() as _, self.inner.get() as _);
578+
}
579+
Self {
580+
inner: ptr,
581+
item: PhantomData,
582+
}
583+
}
584+
}
585+
586+
impl<T: Message> Default for WeakId<T> {
587+
/// Constructs a new `WeakId<T>` that doesn't reference any object.
588+
///
589+
/// Calling [`Self::load`] on the return value always gives [`None`].
590+
fn default() -> Self {
591+
// SAFETY: The pointer is null
592+
unsafe { Self::new_inner(ptr::null_mut()) }
528593
}
529594
}
530595

@@ -534,6 +599,16 @@ unsafe impl<T: Sync + Send> Sync for WeakId<T> {}
534599
/// This implementation follows the same reasoning as `Id<T, Shared>`.
535600
unsafe impl<T: Sync + Send> Send for WeakId<T> {}
536601

602+
// Unsure about the Debug bound on T, see std::sync::Weak
603+
impl<T: fmt::Debug> fmt::Debug for WeakId<T> {
604+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605+
write!(f, "(WeakId)")
606+
}
607+
}
608+
609+
// Underneath this is just a `Box`
610+
impl<T> Unpin for WeakId<T> {}
611+
537612
#[cfg(test)]
538613
mod tests {
539614
use core::mem::size_of;
@@ -569,6 +644,11 @@ mod tests {
569644
size_of::<Option<Id<TestType, Shared>>>(),
570645
size_of::<&TestType>()
571646
);
647+
648+
assert_eq!(
649+
size_of::<Option<WeakId<TestType>>>(),
650+
size_of::<*const ()>()
651+
);
572652
}
573653

574654
#[test]
@@ -611,4 +691,11 @@ mod tests {
611691
drop(obj);
612692
assert!(weak.load().is_none());
613693
}
694+
695+
#[test]
696+
fn test_weak_default() {
697+
let weak: WeakId<Object> = WeakId::default();
698+
assert!(weak.load().is_none());
699+
drop(weak);
700+
}
614701
}

objc2_id/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ assert!(weak.load().is_none());
4141
// Update in Cargo.toml as well.
4242
#![doc(html_root_url = "https://docs.rs/objc2_id/0.1.1")]
4343

44-
#[cfg(alloc)]
4544
extern crate alloc;
4645

4746
pub use id::{Id, Owned, Ownership, ShareId, Shared, WeakId};

0 commit comments

Comments
 (0)