1
- #[ cfg( feature = "alloc" ) ]
2
1
use alloc:: borrow;
2
+ use alloc:: boxed:: Box ;
3
3
use core:: any:: Any ;
4
+ use core:: cell:: UnsafeCell ;
4
5
use core:: fmt;
5
6
use core:: hash;
6
7
use core:: iter:: FusedIterator ;
7
8
use core:: marker:: PhantomData ;
8
9
use core:: mem:: ManuallyDrop ;
9
10
use core:: ops:: { Deref , DerefMut } ;
11
+ use core:: ptr;
10
12
use core:: ptr:: NonNull ;
11
13
12
14
use objc2:: rc:: AutoreleasePool ;
13
- use objc2:: rc:: WeakPtr ;
14
- use objc2:: runtime:: Object ;
15
15
use objc2:: Message ;
16
16
17
17
/// A type used to mark that a struct owns the object(s) it contains,
@@ -60,6 +60,9 @@ impl Ownership for Shared {}
60
60
/// a reference-counting pointer that, when cloned, increases the reference
61
61
/// count.
62
62
///
63
+ /// [`Box`]: alloc::boxed::Box
64
+ /// [`Arc`]: alloc::sync::Arc
65
+ ///
63
66
/// # Caveats
64
67
///
65
68
/// If the inner type implements [`Drop`], that implementation will not be
@@ -179,6 +182,8 @@ impl<T: Message, O: Ownership> Id<T, O> {
179
182
/// Additionally, the pointer must be valid as a reference (aligned,
180
183
/// dereferencable and initialized, see the [`std::ptr`] module for more
181
184
/// information).
185
+ ///
186
+ /// [`std::ptr`]: core::ptr
182
187
//
183
188
// This would be illegal:
184
189
// ```no_run
@@ -456,14 +461,12 @@ impl<I: ExactSizeIterator> ExactSizeIterator for Id<I, Owned> {
456
461
457
462
impl < I : FusedIterator > FusedIterator for Id < I , Owned > { }
458
463
459
- #[ cfg( feature = "alloc" ) ]
460
464
impl < T , O > borrow:: Borrow < T > for Id < T , O > {
461
465
fn borrow ( & self ) -> & T {
462
466
& * * self
463
467
}
464
468
}
465
469
466
- #[ cfg( feature = "alloc" ) ]
467
470
impl < T > borrow:: BorrowMut < T > for Id < T , Owned > {
468
471
fn borrow_mut ( & mut self ) -> & mut T {
469
472
& mut * * self
@@ -502,29 +505,91 @@ pub type ShareId<T> = Id<T, Shared>;
502
505
503
506
/// A pointer type for a weak reference to an Objective-C reference counted
504
507
/// object.
508
+ ///
509
+ /// Allows breaking reference cycles and safely checking whether the object
510
+ /// has been deallocated.
511
+ #[ repr( transparent) ]
505
512
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
507
520
item : PhantomData < T > ,
508
521
}
509
522
510
523
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,
517
543
item : PhantomData ,
518
544
}
519
545
}
520
546
521
- /// Load a [`ShareId`] from the [`WeakId `] if the object still exists.
547
+ /// Load a shared (and retained) [`Id `] if the object still exists.
522
548
///
523
549
/// 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 ( ) ) }
528
593
}
529
594
}
530
595
@@ -534,6 +599,16 @@ unsafe impl<T: Sync + Send> Sync for WeakId<T> {}
534
599
/// This implementation follows the same reasoning as `Id<T, Shared>`.
535
600
unsafe impl < T : Sync + Send > Send for WeakId < T > { }
536
601
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
+
537
612
#[ cfg( test) ]
538
613
mod tests {
539
614
use core:: mem:: size_of;
@@ -569,6 +644,11 @@ mod tests {
569
644
size_of:: <Option <Id <TestType , Shared >>>( ) ,
570
645
size_of:: <& TestType >( )
571
646
) ;
647
+
648
+ assert_eq ! (
649
+ size_of:: <Option <WeakId <TestType >>>( ) ,
650
+ size_of:: <* const ( ) >( )
651
+ ) ;
572
652
}
573
653
574
654
#[ test]
@@ -611,4 +691,11 @@ mod tests {
611
691
drop ( obj) ;
612
692
assert ! ( weak. load( ) . is_none( ) ) ;
613
693
}
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
+ }
614
701
}
0 commit comments