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}