Skip to content

Commit 1171ef8

Browse files
committed
Add an rc module with reference counting primitives
This fixes #24.
1 parent f4f95a4 commit 1171ef8

File tree

9 files changed

+213
-35
lines changed

9 files changed

+213
-35
lines changed

src/exception.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use objc_exception;
22

3-
use id::StrongPtr;
3+
use rc::StrongPtr;
44
use runtime::Object;
55

6-
pub unsafe fn try<F, R>(closure: F) -> Result<R, Option<StrongPtr>>
6+
pub unsafe fn try<F, R>(closure: F) -> Result<R, StrongPtr>
77
where F: FnOnce() -> R {
88
objc_exception::try(closure).map_err(|exception| {
9-
if exception.is_null() { None }
10-
else { Some(StrongPtr::new(exception as *mut Object)) }
9+
StrongPtr::new(exception as *mut Object)
1110
})
1211
}

src/id.rs

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,10 @@ mod macros;
7575

7676
pub mod runtime;
7777
pub mod declare;
78+
pub mod rc;
7879
mod encode;
7980
#[cfg(feature = "exception")]
8081
mod exception;
81-
#[cfg(feature = "exception")]
82-
mod id;
8382
mod message;
8483

8584
#[cfg(test)]

src/message/mod.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ use {Encode, EncodeArguments};
99
#[cfg(feature = "exception")]
1010
macro_rules! objc_try {
1111
($b:block) => (
12-
$crate::exception::try(|| $b).map_err(|exception| match exception {
13-
Some(exception) => MessageError(format!("Uncaught exception {:?}", &*exception)),
14-
None => MessageError("Uncaught exception nil".to_owned()),
15-
})
12+
$crate::exception::try(|| $b).map_err(|exception|
13+
if exception.is_null() {
14+
MessageError("Uncaught exception nil".to_owned())
15+
} else {
16+
MessageError(format!("Uncaught exception {:?}", &**exception))
17+
}
18+
)
1619
)
1720
}
1821

src/rc/mod.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*!
2+
Utilities for reference-counting Objective-C objects.
3+
4+
These utilities provide ARC-like semantics in Rust. They are not intended to
5+
provide a fully safe interface, but can be useful when writing higher-level
6+
Rust wrappers for Objective-C code.
7+
8+
For more information on Objective-C's reference counting, see Apple's documentation:
9+
<https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html>
10+
*/
11+
12+
mod strong;
13+
mod weak;
14+
15+
pub use self::strong::StrongPtr;
16+
pub use self::weak::WeakPtr;
17+
18+
// These tests use NSObject, which isn't present for GNUstep
19+
#[cfg(all(test, any(target_os = "macos", target_os = "ios")))]
20+
mod tests {
21+
use runtime::Object;
22+
use super::StrongPtr;
23+
24+
#[test]
25+
fn test_strong_clone() {
26+
fn retain_count(obj: *mut Object) -> usize {
27+
unsafe { msg_send![obj, retainCount] }
28+
}
29+
30+
let obj = unsafe {
31+
StrongPtr::new(msg_send![class!(NSObject), new])
32+
};
33+
assert!(retain_count(*obj) == 1);
34+
35+
let cloned = obj.clone();
36+
assert!(retain_count(*cloned) == 2);
37+
assert!(retain_count(*obj) == 2);
38+
39+
drop(obj);
40+
assert!(retain_count(*cloned) == 1);
41+
}
42+
43+
#[test]
44+
fn test_weak() {
45+
let obj = unsafe {
46+
StrongPtr::new(msg_send![class!(NSObject), new])
47+
};
48+
let weak = obj.weak();
49+
50+
let strong = weak.load();
51+
assert!(*strong == *obj);
52+
drop(strong);
53+
54+
drop(obj);
55+
assert!(weak.load().is_null());
56+
}
57+
58+
#[test]
59+
fn test_weak_copy() {
60+
let obj = unsafe {
61+
StrongPtr::new(msg_send![class!(NSObject), new])
62+
};
63+
let weak = obj.weak();
64+
65+
let weak2 = weak.clone();
66+
let strong = weak2.load();
67+
assert!(*strong == *obj);
68+
}
69+
}

src/rc/strong.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use std::fmt;
2+
use std::mem;
3+
use std::ops::Deref;
4+
5+
use runtime::{Object, self};
6+
use super::WeakPtr;
7+
8+
/// A pointer that strongly references an object, ensuring it won't be deallocated.
9+
pub struct StrongPtr(*mut Object);
10+
11+
impl StrongPtr {
12+
/// Constructs a `StrongPtr` to a newly created object that already has a
13+
/// +1 retain count. This will not retain the object.
14+
/// When dropped, the object will be released.
15+
/// Unsafe because the caller must ensure the given object pointer is valid.
16+
pub unsafe fn new(ptr: *mut Object) -> Self {
17+
StrongPtr(ptr)
18+
}
19+
20+
/// Retains the given object and constructs a `StrongPtr` to it.
21+
/// When dropped, the object will be released.
22+
/// Unsafe because the caller must ensure the given object pointer is valid.
23+
pub unsafe fn retain(ptr: *mut Object) -> Self {
24+
StrongPtr(runtime::objc_retain(ptr))
25+
}
26+
27+
/// Autoreleases self, meaning that the object is not immediately released,
28+
/// but will be when the autorelease pool is drained. A pointer to the
29+
/// object is returned, but its validity is no longer ensured.
30+
pub fn autorelease(self) -> *mut Object {
31+
let ptr = self.0;
32+
mem::forget(self);
33+
unsafe {
34+
runtime::objc_autorelease(ptr);
35+
}
36+
ptr
37+
}
38+
39+
/// Returns a `WeakPtr` to self.
40+
pub fn weak(&self) -> WeakPtr {
41+
unsafe { WeakPtr::new(self.0) }
42+
}
43+
}
44+
45+
impl Drop for StrongPtr {
46+
fn drop(&mut self) {
47+
unsafe {
48+
runtime::objc_release(self.0);
49+
}
50+
}
51+
}
52+
53+
impl Clone for StrongPtr {
54+
fn clone(&self) -> StrongPtr {
55+
unsafe {
56+
StrongPtr::retain(self.0)
57+
}
58+
}
59+
}
60+
61+
impl Deref for StrongPtr {
62+
type Target = *mut Object;
63+
64+
fn deref(&self) -> &*mut Object {
65+
&self.0
66+
}
67+
}
68+
69+
impl fmt::Pointer for StrongPtr {
70+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71+
fmt::Pointer::fmt(&self.0, f)
72+
}
73+
}

src/rc/weak.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use std::cell::UnsafeCell;
2+
use std::ptr;
3+
4+
use runtime::{Object, self};
5+
use super::StrongPtr;
6+
7+
// Our pointer must have the same address even if we are moved, so Box it.
8+
// Although loading the WeakPtr may modify the pointer, it is thread safe,
9+
// so we must use an UnsafeCell to get a *mut without self being mutable.
10+
11+
/// A pointer that weakly references an object, allowing to safely check
12+
/// whether it has been deallocated.
13+
pub struct WeakPtr(Box<UnsafeCell<*mut Object>>);
14+
15+
impl WeakPtr {
16+
/// Constructs a `WeakPtr` to the given object.
17+
/// Unsafe because the caller must ensure the given object pointer is valid.
18+
pub unsafe fn new(obj: *mut Object) -> Self {
19+
let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
20+
runtime::objc_initWeak(ptr.get(), obj);
21+
WeakPtr(ptr)
22+
}
23+
24+
/// Loads the object self points to, returning a `StrongPtr`.
25+
/// If the object has been deallocated, the returned pointer will be null.
26+
pub fn load(&self) -> StrongPtr {
27+
unsafe {
28+
let ptr = runtime::objc_loadWeakRetained(self.0.get());
29+
StrongPtr::new(ptr)
30+
}
31+
}
32+
}
33+
34+
impl Drop for WeakPtr {
35+
fn drop(&mut self) {
36+
unsafe {
37+
runtime::objc_destroyWeak(self.0.get());
38+
}
39+
}
40+
}
41+
42+
impl Clone for WeakPtr {
43+
fn clone(&self) -> Self {
44+
let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
45+
unsafe {
46+
runtime::objc_copyWeak(ptr.get(), self.0.get());
47+
}
48+
WeakPtr(ptr)
49+
}
50+
}

src/runtime.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,15 @@ extern {
128128
pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
129129
pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
130130
pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
131+
132+
pub fn objc_retain(obj: *mut Object) -> *mut Object;
133+
pub fn objc_release(obj: *mut Object);
134+
pub fn objc_autorelease(obj: *mut Object);
135+
136+
pub fn objc_loadWeakRetained(location: *mut *mut Object) -> *mut Object;
137+
pub fn objc_initWeak(location: *mut *mut Object, obj: *mut Object) -> *mut Object;
138+
pub fn objc_destroyWeak(location: *mut *mut Object);
139+
pub fn objc_copyWeak(to: *mut *mut Object, from: *mut *mut Object);
131140
}
132141

133142
impl Sel {

tests-ios/prelude.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ extern crate objc;
33

44
pub use objc::*;
55
use objc::runtime::*;
6+
use objc::rc::*;
67

78
#[path = "../src/test_utils.rs"]
89
mod test_utils;

0 commit comments

Comments
 (0)