Skip to main content

mtl_foundation/
object.rs

1//! Base object traits for Foundation.
2//!
3//! Corresponds to `Foundation/NSObject.hpp`.
4//!
5//! # C++ Equivalent
6//!
7//! ```cpp
8//! namespace NS {
9//! template <class _Class, class _Base = class Object>
10//! class Referencing : public _Base {
11//! public:
12//!     _Class*  retain();
13//!     void     release();
14//!     _Class*  autorelease();
15//!     UInteger retainCount() const;
16//! };
17//!
18//! template <class _Class, class _Base = class Object>
19//! class Copying : public Referencing<_Class, _Base> {
20//! public:
21//!     _Class* copy() const;
22//! };
23//!
24//! template <class _Class, class _Base = class Object>
25//! class SecureCoding : public Referencing<_Class, _Base> {};
26//!
27//! class Object : public Referencing<Object, objc_object> {
28//! public:
29//!     UInteger      hash() const;
30//!     bool          isEqual(const Object* pObject) const;
31//!     class String* description() const;
32//!     class String* debugDescription() const;
33//! };
34//! }
35//! ```
36
37use std::ffi::c_void;
38use std::ptr::NonNull;
39
40use crate::types::UInteger;
41use mtl_sys::{Class, Sel, msg_send_0, msg_send_1, sel};
42
43/// Base trait for all Objective-C objects that support reference counting.
44///
45/// C++ equivalent: `NS::Referencing<_Class, _Base>`
46pub trait Referencing: Sized {
47    /// Get the raw pointer to the Objective-C object.
48    fn as_ptr(&self) -> *const c_void;
49
50    /// Get the raw mutable pointer to the Objective-C object.
51    fn as_mut_ptr(&self) -> *mut c_void {
52        self.as_ptr() as *mut c_void
53    }
54
55    /// Retain the object, incrementing its reference count.
56    ///
57    /// C++ equivalent: `_Class* retain()`
58    fn retain(&self) -> Self
59    where
60        Self: Clone,
61    {
62        unsafe {
63            let _: *mut c_void = msg_send_0(self.as_ptr(), sel!(retain));
64        }
65        self.clone()
66    }
67
68    /// Release the object, decrementing its reference count.
69    ///
70    /// C++ equivalent: `void release()`
71    fn release(&self) {
72        unsafe {
73            msg_send_0::<()>(self.as_ptr(), sel!(release));
74        }
75    }
76
77    /// Autorelease the object.
78    ///
79    /// C++ equivalent: `_Class* autorelease()`
80    fn autorelease(&self) -> Self
81    where
82        Self: Clone,
83    {
84        unsafe {
85            let _: *mut c_void = msg_send_0(self.as_ptr(), sel!(autorelease));
86        }
87        self.clone()
88    }
89
90    /// Get the retain count of the object.
91    ///
92    /// C++ equivalent: `UInteger retainCount() const`
93    fn retain_count(&self) -> UInteger {
94        unsafe { msg_send_0(self.as_ptr(), sel!(retainCount)) }
95    }
96}
97
98/// Trait for objects that support copying.
99///
100/// C++ equivalent: `NS::Copying<_Class, _Base>`
101pub trait Copying: Referencing {
102    /// Create a copy of this object.
103    ///
104    /// C++ equivalent: `_Class* copy() const`
105    fn copy(&self) -> Option<Self>
106    where
107        Self: Sized;
108}
109
110/// Trait for objects that support secure coding.
111///
112/// C++ equivalent: `NS::SecureCoding<_Class, _Base>`
113pub trait SecureCoding: Referencing {}
114
115/// The root class of most Objective-C class hierarchies.
116///
117/// C++ equivalent: `NS::Object`
118#[repr(transparent)]
119#[derive(Clone)]
120pub struct Object(NonNull<c_void>);
121
122impl Object {
123    /// Create an Object from a raw pointer.
124    ///
125    /// # Safety
126    ///
127    /// The pointer must be a valid Objective-C object.
128    #[inline]
129    pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> {
130        NonNull::new(ptr).map(Self)
131    }
132
133    /// Get the hash value of this object.
134    ///
135    /// C++ equivalent: `UInteger hash() const`
136    #[inline]
137    pub fn hash(&self) -> UInteger {
138        unsafe { msg_send_0(self.as_ptr(), sel!(hash)) }
139    }
140
141    /// Check if this object is equal to another.
142    ///
143    /// C++ equivalent: `bool isEqual(const Object* pObject) const`
144    #[inline]
145    pub fn is_equal(&self, other: &Object) -> bool {
146        unsafe { msg_send_1(self.as_ptr(), sel!(isEqual:), other.as_ptr()) }
147    }
148
149    /// Get a string description of this object.
150    ///
151    /// C++ equivalent: `class String* description() const`
152    #[inline]
153    pub fn description(&self) -> *mut c_void {
154        unsafe { msg_send_0(self.as_ptr(), sel!(description)) }
155    }
156
157    /// Get a debug description of this object.
158    ///
159    /// C++ equivalent: `class String* debugDescription() const`
160    #[inline]
161    pub fn debug_description(&self) -> *mut c_void {
162        unsafe { msg_send_0(self.as_ptr(), sel!(debugDescription)) }
163    }
164
165    /// Allocate an instance of a class.
166    ///
167    /// C++ equivalent: `template <class _Class> static _Class* alloc(const void* pClass)`
168    #[inline]
169    pub fn alloc_with_class(cls: Class) -> Option<Self> {
170        unsafe {
171            let ptr: *mut c_void = msg_send_0(cls.as_ptr(), sel!(alloc));
172            Self::from_ptr(ptr)
173        }
174    }
175
176    /// Initialize an allocated object.
177    ///
178    /// C++ equivalent: `template <class _Class> _Class* init()`
179    #[inline]
180    pub fn init(&self) -> Option<Self> {
181        unsafe {
182            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(init));
183            Self::from_ptr(ptr)
184        }
185    }
186
187    /// Check if this object responds to a selector.
188    #[inline]
189    pub fn responds_to_selector(&self, selector: Sel) -> bool {
190        unsafe { msg_send_1(self.as_ptr(), sel!(respondsToSelector:), selector) }
191    }
192}
193
194impl Referencing for Object {
195    #[inline]
196    fn as_ptr(&self) -> *const c_void {
197        self.0.as_ptr()
198    }
199}
200
201// SAFETY: Objective-C objects are thread-safe for reference counting
202unsafe impl Send for Object {}
203unsafe impl Sync for Object {}
204
205/// Helper function to send a message with the given selector.
206///
207/// This is the Rust equivalent of C++'s `Object::sendMessage<>`.
208///
209/// # Safety
210///
211/// - `obj` must be a valid Objective-C object pointer
212/// - `sel` must be a valid selector for the object
213/// - The return type must match the actual return type of the method
214#[inline]
215pub unsafe fn send_message<R>(obj: *const c_void, sel: Sel) -> R {
216    unsafe { msg_send_0(obj, sel) }
217}
218
219/// Helper function to send a message with one argument.
220///
221/// # Safety
222///
223/// See `send_message` for safety requirements.
224#[inline]
225pub unsafe fn send_message_1<R, A>(obj: *const c_void, sel: Sel, a: A) -> R {
226    unsafe { msg_send_1(obj, sel, a) }
227}
228
229#[cfg(test)]
230mod tests {
231    use super::*;
232
233    #[test]
234    fn test_object_size() {
235        // Object should be pointer-sized
236        assert_eq!(
237            std::mem::size_of::<Object>(),
238            std::mem::size_of::<*mut c_void>()
239        );
240    }
241}