Skip to content

Commit e04a4e7

Browse files
committed
Add ::rc::Retained, the smart pointer version of StrongPtr
1 parent c8696b0 commit e04a4e7

File tree

2 files changed

+247
-19
lines changed

2 files changed

+247
-19
lines changed

src/rc/mod.rs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ assert!(weak.load().is_null());
4040
```
4141
*/
4242

43+
mod retained;
4344
mod strong;
4445
mod weak;
4546
mod autorelease;
4647

48+
pub use self::retained::Retained;
4749
pub use self::strong::StrongPtr;
4850
pub use self::weak::WeakPtr;
4951
pub use self::autorelease::autoreleasepool;
@@ -55,25 +57,6 @@ mod tests {
5557
use super::StrongPtr;
5658
use super::autoreleasepool;
5759

58-
#[test]
59-
fn test_strong_clone() {
60-
fn retain_count(obj: *mut Object) -> usize {
61-
unsafe { msg_send![obj, retainCount] }
62-
}
63-
64-
let obj = unsafe {
65-
StrongPtr::new(msg_send![class!(NSObject), new])
66-
};
67-
assert!(retain_count(*obj) == 1);
68-
69-
let cloned = obj.clone();
70-
assert!(retain_count(*cloned) == 2);
71-
assert!(retain_count(*obj) == 2);
72-
73-
drop(obj);
74-
assert!(retain_count(*cloned) == 1);
75-
}
76-
7760
#[test]
7861
fn test_weak() {
7962
let obj = unsafe {

src/rc/retained.rs

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
use core::borrow;
2+
use core::fmt;
3+
use core::hash;
4+
use core::marker::{PhantomData, Unpin};
5+
use core::mem;
6+
use core::ops::Deref;
7+
use core::ptr::NonNull;
8+
9+
use crate::runtime::{self, Object};
10+
11+
/// A smart pointer that strongly references an object, ensuring it won't be
12+
/// deallocated.
13+
///
14+
/// This is guaranteed to have the same size as the underlying pointer.
15+
///
16+
/// ## Caveats
17+
///
18+
/// If the inner type implements [`Drop`], that implementation will not be
19+
/// called, since there is no way to ensure that the Objective-C runtime will
20+
/// do so. If you need to run some code when the object is destroyed,
21+
/// implement the `dealloc` selector instead.
22+
///
23+
/// TODO: Explain similarities with `Arc` and `RefCell`.
24+
#[repr(transparent)]
25+
pub struct Retained<T> {
26+
/// A pointer to the contained object.
27+
///
28+
/// It is important that this is `NonNull`, since we want to dereference
29+
/// it later.
30+
///
31+
/// Usually the contained object would be an [extern type][extern-type-rfc]
32+
/// (when that gets stabilized), or a type such as:
33+
/// ```
34+
/// pub struct MyType {
35+
/// _data: [u8; 0], // TODO: `UnsafeCell`?
36+
/// }
37+
/// ```
38+
///
39+
/// DSTs that carry metadata cannot be used here, so unsure if we should
40+
/// have a `?Sized` bound?
41+
///
42+
/// TODO:
43+
/// https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
44+
/// https://doc.rust-lang.org/nomicon/exotic-sizes.html
45+
/// https://doc.rust-lang.org/core/ptr/trait.Pointee.html
46+
/// https://doc.rust-lang.org/core/ptr/traitalias.Thin.html
47+
///
48+
/// [extern-type-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1861-extern-types.md
49+
ptr: NonNull<T>, // Covariant
50+
phantom: PhantomData<T>,
51+
}
52+
53+
impl<T> Retained<T> {
54+
/// Constructs a `Retained<T>` to an object that already has a +1 retain
55+
/// count. This will not retain the object.
56+
///
57+
/// When dropped, the object will be released.
58+
///
59+
/// # Safety
60+
///
61+
/// The caller must ensure the given object pointer is valid, and has +1
62+
/// retain count.
63+
///
64+
/// TODO: Something about there not being any mutable references.
65+
#[inline]
66+
pub const unsafe fn new(ptr: NonNull<T>) -> Self {
67+
Retained {
68+
ptr,
69+
phantom: PhantomData,
70+
}
71+
}
72+
73+
#[inline]
74+
pub const fn as_ptr(&self) -> *mut T {
75+
self.ptr.as_ptr()
76+
}
77+
78+
/// Retains the given object pointer.
79+
///
80+
/// When dropped, the object will be released.
81+
///
82+
/// # Safety
83+
///
84+
/// The caller must ensure the given object pointer is valid.
85+
#[doc(alias = "objc_retain")]
86+
#[inline]
87+
pub unsafe fn retain(ptr: NonNull<T>) -> Self {
88+
// SAFETY: The caller upholds that the pointer is valid
89+
let rtn = runtime::objc_retain(ptr.as_ptr() as *mut Object);
90+
debug_assert_eq!(rtn, ptr.as_ptr() as *mut Object);
91+
Retained {
92+
ptr,
93+
phantom: PhantomData,
94+
}
95+
}
96+
97+
/// Autoreleases the retained pointer, meaning that the object is not
98+
/// immediately released, but will be when the innermost / current
99+
/// autorelease pool is drained.
100+
///
101+
/// A pointer to the object is returned, but it's validity is only until
102+
/// guaranteed until the innermost pool is drained.
103+
#[doc(alias = "objc_autorelease")]
104+
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
105+
#[inline]
106+
pub fn autorelease(self) -> NonNull<T> {
107+
let ptr = mem::ManuallyDrop::new(self).ptr;
108+
// SAFETY: The `ptr` is guaranteed to be valid and have at least one
109+
// retain count.
110+
// And because of the ManuallyDrop, we don't call the Drop
111+
// implementation, so the object won't also be released there.
112+
unsafe { runtime::objc_autorelease(ptr.as_ptr() as *mut Object) };
113+
ptr
114+
}
115+
116+
#[cfg(test)]
117+
#[doc(alias = "retainCount")]
118+
pub fn retain_count(&self) -> usize {
119+
unsafe { msg_send![self.ptr.as_ptr() as *mut Object, retainCount] }
120+
}
121+
}
122+
123+
// TODO: #[may_dangle]
124+
// https://doc.rust-lang.org/nightly/nomicon/dropck.html
125+
impl<T> Drop for Retained<T> {
126+
/// Releases the retained object
127+
#[doc(alias = "objc_release")]
128+
#[doc(alias = "release")]
129+
#[inline]
130+
fn drop(&mut self) {
131+
// SAFETY: The `ptr` is guaranteed to be valid and have at least one
132+
// retain count
133+
unsafe { runtime::objc_release(self.ptr.as_ptr() as *mut Object) };
134+
}
135+
}
136+
137+
impl<T> Clone for Retained<T> {
138+
/// Makes a clone of the `Retained` object.
139+
///
140+
/// This increases the object's reference count.
141+
#[doc(alias = "objc_retain")]
142+
#[doc(alias = "retain")]
143+
#[inline]
144+
fn clone(&self) -> Self {
145+
// SAFETY: The `ptr` is guaranteed to be valid
146+
unsafe { Self::retain(self.ptr) }
147+
}
148+
}
149+
150+
impl<T> Deref for Retained<T> {
151+
type Target = T;
152+
153+
#[inline]
154+
fn deref(&self) -> &Self::Target {
155+
// SAFETY: TODO
156+
unsafe { self.ptr.as_ref() }
157+
}
158+
}
159+
160+
impl<T: PartialEq> PartialEq for Retained<T> {
161+
#[inline]
162+
fn eq(&self, other: &Retained<T>) -> bool {
163+
&**self == &**other
164+
}
165+
166+
#[inline]
167+
fn ne(&self, other: &Retained<T>) -> bool {
168+
&**self != &**other
169+
}
170+
}
171+
172+
// TODO: impl PartialOrd, Ord and Eq
173+
174+
impl<T: fmt::Display> fmt::Display for Retained<T> {
175+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176+
fmt::Display::fmt(&**self, f)
177+
}
178+
}
179+
180+
impl<T: fmt::Debug> fmt::Debug for Retained<T> {
181+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182+
fmt::Debug::fmt(&**self, f)
183+
}
184+
}
185+
186+
impl<T> fmt::Pointer for Retained<T> {
187+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188+
fmt::Pointer::fmt(&self.as_ptr(), f)
189+
}
190+
}
191+
192+
impl<T: hash::Hash> hash::Hash for Retained<T> {
193+
fn hash<H: hash::Hasher>(&self, state: &mut H) {
194+
(&**self).hash(state)
195+
}
196+
}
197+
198+
impl<T> borrow::Borrow<T> for Retained<T> {
199+
fn borrow(&self) -> &T {
200+
&**self
201+
}
202+
}
203+
204+
impl<T> AsRef<T> for Retained<T> {
205+
fn as_ref(&self) -> &T {
206+
&**self
207+
}
208+
}
209+
210+
impl<T> Unpin for Retained<T> {}
211+
212+
#[cfg(test)]
213+
mod tests {
214+
use std::mem::size_of;
215+
216+
use super::Retained;
217+
use crate::runtime::Object;
218+
219+
pub struct TestType {
220+
_data: [u8; 0], // TODO: `UnsafeCell`?
221+
}
222+
223+
#[test]
224+
fn test_size_of() {
225+
assert_eq!(size_of::<Retained<TestType>>(), size_of::<&TestType>());
226+
assert_eq!(
227+
size_of::<Option<Retained<TestType>>>(),
228+
size_of::<&TestType>()
229+
);
230+
}
231+
232+
#[cfg(any(target_os = "macos", target_os = "ios"))]
233+
#[test]
234+
fn test_clone() {
235+
let obj: Retained<Object> = unsafe { Retained::new(msg_send![class!(NSObject), new]) };
236+
assert!(obj.retain_count() == 1);
237+
238+
let cloned = obj.clone();
239+
assert!(cloned.retain_count() == 2);
240+
assert!(obj.retain_count() == 2);
241+
242+
drop(obj);
243+
assert!(cloned.retain_count() == 1);
244+
}
245+
}

0 commit comments

Comments
 (0)