Skip to main content

mtl_foundation/
autorelease.rs

1//! Autorelease pool for Foundation.
2//!
3//! Corresponds to `Foundation/NSAutoreleasePool.hpp`.
4//!
5//! # C++ Equivalent
6//!
7//! ```cpp
8//! namespace NS {
9//! class AutoreleasePool : public Object {
10//! public:
11//!     static AutoreleasePool* alloc();
12//!     AutoreleasePool*        init();
13//!     void                    drain();
14//!     void                    addObject(Object* pObject);
15//!     static void             showPools();
16//! };
17//! }
18//! ```
19
20use std::ffi::c_void;
21use std::ptr::NonNull;
22
23use mtl_sys::{class, msg_send_0, msg_send_1, sel};
24
25use crate::object::{Object, Referencing};
26
27/// An Objective-C autorelease pool.
28///
29/// C++ equivalent: `NS::AutoreleasePool`
30#[repr(transparent)]
31#[derive(Clone)]
32pub struct AutoreleasePool(NonNull<c_void>);
33
34impl AutoreleasePool {
35    /// Allocate a new autorelease pool.
36    ///
37    /// C++ equivalent: `static AutoreleasePool* alloc()`
38    #[inline]
39    pub fn alloc() -> Option<Self> {
40        unsafe {
41            let ptr: *mut c_void = msg_send_0(class!(NSAutoreleasePool).as_ptr(), sel!(alloc));
42            Self::from_ptr(ptr)
43        }
44    }
45
46    /// Initialize an allocated autorelease pool.
47    ///
48    /// C++ equivalent: `AutoreleasePool* init()`
49    #[inline]
50    pub fn init(&self) -> Option<Self> {
51        unsafe {
52            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(init));
53            Self::from_ptr(ptr)
54        }
55    }
56
57    /// Drain the autorelease pool.
58    ///
59    /// C++ equivalent: `void drain()`
60    #[inline]
61    pub fn drain(&self) {
62        unsafe {
63            msg_send_0::<()>(self.as_ptr(), sel!(drain));
64        }
65    }
66
67    /// Add an object to the autorelease pool.
68    ///
69    /// C++ equivalent: `void addObject(Object* pObject)`
70    #[inline]
71    pub fn add_object(&self, object: &Object) {
72        unsafe {
73            let _: () = msg_send_1(self.as_ptr(), sel!(addObject:), object.as_ptr());
74        }
75    }
76
77    /// Show all autorelease pools (for debugging).
78    ///
79    /// C++ equivalent: `static void showPools()`
80    #[inline]
81    pub fn show_pools() {
82        unsafe {
83            msg_send_0::<()>(class!(NSAutoreleasePool).as_ptr(), sel!(showPools));
84        }
85    }
86
87    /// Create an AutoreleasePool from a raw pointer.
88    ///
89    /// # Safety
90    ///
91    /// The pointer must be a valid Objective-C NSAutoreleasePool object.
92    #[inline]
93    pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> {
94        NonNull::new(ptr).map(Self)
95    }
96
97    /// Create a new autorelease pool (convenience method).
98    ///
99    /// This is a convenience method that allocates and initializes a pool.
100    #[inline]
101    pub fn new() -> Option<Self> {
102        Self::alloc()?.init()
103    }
104}
105
106impl Referencing for AutoreleasePool {
107    #[inline]
108    fn as_ptr(&self) -> *const c_void {
109        self.0.as_ptr()
110    }
111}
112
113// SAFETY: NSAutoreleasePool is thread-safe
114unsafe impl Send for AutoreleasePool {}
115unsafe impl Sync for AutoreleasePool {}
116
117impl std::fmt::Debug for AutoreleasePool {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        f.debug_struct("AutoreleasePool")
120            .field("ptr", &self.0)
121            .finish()
122    }
123}
124
125impl Drop for AutoreleasePool {
126    fn drop(&mut self) {
127        self.drain();
128    }
129}
130
131/// RAII wrapper for autorelease pool scopes.
132///
133/// This provides a more Rust-idiomatic way to use autorelease pools.
134pub struct AutoreleasePoolScope {
135    pool: AutoreleasePool,
136}
137
138impl AutoreleasePoolScope {
139    /// Create a new autorelease pool scope.
140    #[inline]
141    pub fn new() -> Option<Self> {
142        AutoreleasePool::new().map(|pool| Self { pool })
143    }
144
145    /// Get a reference to the underlying pool.
146    #[inline]
147    pub fn pool(&self) -> &AutoreleasePool {
148        &self.pool
149    }
150}
151
152impl Default for AutoreleasePoolScope {
153    fn default() -> Self {
154        Self::new().expect("Failed to create autorelease pool")
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn test_autorelease_pool_size() {
164        // AutoreleasePool should be pointer-sized
165        assert_eq!(
166            std::mem::size_of::<AutoreleasePool>(),
167            std::mem::size_of::<*mut c_void>()
168        );
169    }
170}