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}