Skip to main content

objc_rs/rc/
mod.rs

1/*!
2Utilities for reference counting Objective-C objects.
3
4The utilities of the `rc` module provide ARC-like semantics for working with
5Objective-C's reference counted objects in Rust.
6A `StrongPtr` retains an object and releases the object when dropped.
7A `WeakPtr` will not retain the object, but can be upgraded to a `StrongPtr`
8and safely fails if the object has been deallocated.
9
10These utilities are not intended to provide a fully safe interface, but can be
11useful when writing higher-level Rust wrappers for Objective-C code.
12
13For more information on Objective-C's reference counting, see Apple's documentation:
14<https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html>
15
16# Example
17
18``` no_run
19# #[macro_use] extern crate objc;
20# use objc::rc::{autoreleasepool, StrongPtr};
21# fn main() {
22// StrongPtr will release the object when dropped
23let obj = unsafe {
24    StrongPtr::new(msg_send![class!(NSObject), new])
25};
26
27// Cloning retains the object an additional time
28let cloned = obj.clone();
29autoreleasepool(|| {
30    // Autorelease consumes the StrongPtr, but won't
31    // actually release until the end of an autoreleasepool
32    cloned.autorelease();
33});
34
35// Weak references won't retain the object
36let weak = obj.weak();
37drop(obj);
38assert!(weak.load().is_null());
39# }
40```
41*/
42
43mod autorelease;
44mod strong;
45mod weak;
46
47pub use self::autorelease::autoreleasepool;
48pub use self::strong::StrongPtr;
49pub use self::weak::WeakPtr;
50
51// These tests use NSObject, which isn't present for GNUstep
52#[cfg(all(test, any(target_os = "macos", target_os = "ios")))]
53mod tests {
54    use super::StrongPtr;
55    use super::autoreleasepool;
56    use crate::runtime::Object;
57
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 { StrongPtr::new(msg_send![class!(NSObject), new]) };
65        assert!(retain_count(*obj) == 1);
66
67        let cloned = obj.clone();
68        assert!(retain_count(*cloned) == 2);
69        assert!(retain_count(*obj) == 2);
70
71        drop(obj);
72        assert!(retain_count(*cloned) == 1);
73    }
74
75    #[test]
76    fn test_weak() {
77        let obj = unsafe { StrongPtr::new(msg_send![class!(NSObject), new]) };
78        let weak = obj.weak();
79
80        let strong = weak.load();
81        assert!(*strong == *obj);
82        drop(strong);
83
84        drop(obj);
85        assert!(weak.load().is_null());
86    }
87
88    #[test]
89    fn test_weak_copy() {
90        let obj = unsafe { StrongPtr::new(msg_send![class!(NSObject), new]) };
91        let weak = obj.weak();
92
93        let weak2 = weak.clone();
94        let strong = weak2.load();
95        assert!(*strong == *obj);
96    }
97
98    #[test]
99    fn test_autorelease() {
100        let obj = unsafe { StrongPtr::new(msg_send![class!(NSObject), new]) };
101
102        fn retain_count(obj: *mut Object) -> usize {
103            unsafe { msg_send![obj, retainCount] }
104        }
105        let cloned = obj.clone();
106
107        autoreleasepool(|| {
108            obj.autorelease();
109            assert!(retain_count(*cloned) == 2);
110        });
111
112        // make sure that the autoreleased value has been released
113        assert!(retain_count(*cloned) == 1);
114    }
115}