objc2/runtime/
nsobject.rs

1use core::fmt;
2use core::hash;
3use core::ops::Deref;
4use core::panic::{RefUnwindSafe, UnwindSafe};
5
6use crate::ffi::NSUInteger;
7use crate::rc::{Allocated, DefaultRetained, Retained};
8use crate::runtime::{AnyClass, AnyObject, AnyProtocol, ImplementedBy, ProtocolObject, Sel};
9use crate::DowncastTarget;
10use crate::{extern_methods, msg_send, AllocAnyThread, ClassType, Message, ProtocolType};
11
12/// The root class of most Objective-C class hierarchies.
13///
14/// This represents the [`NSObject` class][cls]. The name "NSObject" also
15/// refers to a protocol, see [`NSObjectProtocol`] for that.
16///
17/// This class has been defined in `objc` since macOS 10.8, but is also
18/// re-exported under `objc2_foundation::NSObject`, you might want to use that
19/// path instead.
20///
21/// [cls]: https://developer.apple.com/documentation/objectivec/nsobject?language=objc
22#[repr(C)]
23pub struct NSObject {
24    __superclass: AnyObject,
25}
26
27// Would be _super_ nice to have this kind of impl, but that isn't possible.
28// impl Unsize<AnyObject> for NSObject {}
29
30crate::__extern_class_impl_traits! {
31    ()
32    (unsafe impl)
33    (NSObject)
34    (AnyObject)
35}
36
37// We do not want to expose this type publicly, even though it's exposed in
38// the trait impl.
39mod private {
40    #[derive(PartialEq, Eq, Hash)] // Delegate to NSObject
41    pub struct ForDefinedSubclasses(pub(super) super::NSObject);
42}
43
44impl fmt::Debug for private::ForDefinedSubclasses {
45    #[inline]
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        // Delegate to NSObject
48        fmt::Debug::fmt(&**self, f)
49    }
50}
51
52impl Deref for private::ForDefinedSubclasses {
53    type Target = NSObject;
54
55    #[inline]
56    fn deref(&self) -> &Self::Target {
57        &self.0
58    }
59}
60
61// SAFETY: `NSObject` is thread safe by itself, the only reason why it
62// isn't marked `Send` is because its subclasses may not be thread safe.
63//
64// But any subclass of NSObject that the user creates is thread safe, provided
65// that their ivars are thread safe too, and the class is not configured as
66// `MainThreadOnly`.
67//
68// This special casing of NSObject is similar to what Swift does:
69// https://developer.apple.com/documentation/swift/sendable#Sendable-Classes
70unsafe impl Send for private::ForDefinedSubclasses {}
71// SAFETY: Same as above.
72unsafe impl Sync for private::ForDefinedSubclasses {}
73
74// NSObject itself is also unwind safe, and so subclasses should be too,
75// unless they use e.g. interior mutability in their ivars.
76impl UnwindSafe for private::ForDefinedSubclasses {}
77impl RefUnwindSafe for private::ForDefinedSubclasses {}
78
79unsafe impl ClassType for NSObject {
80    type Super = AnyObject;
81    type ThreadKind = dyn AllocAnyThread;
82    const NAME: &'static str = "NSObject";
83
84    #[inline]
85    fn class() -> &'static AnyClass {
86        crate::__class_inner!("NSObject", "NSObject")
87    }
88
89    #[inline]
90    fn as_super(&self) -> &Self::Super {
91        &self.__superclass
92    }
93
94    const __INNER: () = ();
95
96    // Defined subclasses can assume more lenient auto traits.
97    type __SubclassingType = private::ForDefinedSubclasses;
98}
99
100unsafe impl DowncastTarget for NSObject {}
101
102/// The methods that are fundamental to most Objective-C objects.
103///
104/// This represents the [`NSObject` protocol][proto].
105///
106/// You should rarely need to use this for anything other than as a trait
107/// bound in [`extern_protocol!`], to allow your protocol to implement `Debug`
108/// `Hash`, `PartialEq` and `Eq`.
109///
110/// This trait is exported under `objc2_foundation::NSObjectProtocol`, you
111/// probably want to use that path instead.
112///
113/// [proto]: https://developer.apple.com/documentation/objectivec/1418956-nsobject?language=objc
114/// [`extern_protocol!`]: crate::extern_protocol!
115///
116///
117/// # Safety
118///
119/// Like with [other protocols](ProtocolType), the type must represent a class
120/// that implements the `NSObject` protocol.
121#[allow(non_snake_case)] // Follow the naming scheme in framework crates
122pub unsafe trait NSObjectProtocol {
123    /// Check whether the object is equal to an arbitrary other object.
124    ///
125    /// Most objects that implement `NSObjectProtocol` also implements the
126    /// [`PartialEq`] trait. If the objects you are comparing are of the same
127    /// type, you likely want to use that instead.
128    ///
129    /// See [Apple's documentation][apple-doc] for details.
130    ///
131    /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418795-isequal?language=objc
132    #[doc(alias = "isEqual:")]
133    fn isEqual(&self, other: Option<&AnyObject>) -> bool
134    where
135        Self: Sized + Message,
136    {
137        unsafe { msg_send![self, isEqual: other] }
138    }
139
140    /// An integer that can be used as a table address in a hash table
141    /// structure.
142    ///
143    /// Most objects that implement `NSObjectProtocol` also implements the
144    /// [`Hash`][std::hash::Hash] trait, you likely want to use that instead.
145    ///
146    /// See [Apple's documentation][apple-doc] for details.
147    ///
148    /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418859-hash?language=objc
149    fn hash(&self) -> NSUInteger
150    where
151        Self: Sized + Message,
152    {
153        unsafe { msg_send![self, hash] }
154    }
155
156    /// Check if the object is an instance of the class, or one of its
157    /// subclasses.
158    ///
159    /// See [`AnyObject::downcast_ref`] or [`Retained::downcast`] if your
160    /// intention is to use this to cast an object to another, and see
161    /// [Apple's documentation][apple-doc] for more details on what you may
162    /// (and what you may not) do with this information in general.
163    ///
164    /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418511-iskindofclass?language=objc
165    #[doc(alias = "isKindOfClass:")]
166    fn isKindOfClass(&self, cls: &AnyClass) -> bool
167    where
168        Self: Sized + Message,
169    {
170        unsafe { msg_send![self, isKindOfClass: cls] }
171    }
172
173    /// Check if the object is an instance of the class type, or one of its
174    /// subclasses.
175    ///
176    /// See [`isKindOfClass`][Self::isKindOfClass] for details.
177    #[deprecated = "use `isKindOfClass` directly, or cast your objects with `AnyObject::downcast_ref`"]
178    // TODO: Use extern_protocol! once we get rid of this
179    fn is_kind_of<T: ClassType>(&self) -> bool
180    where
181        Self: Sized + Message,
182    {
183        self.isKindOfClass(T::class())
184    }
185
186    /// Check if the object is an instance of a specific class, without
187    /// checking subclasses.
188    ///
189    /// Note that this is rarely what you want, the specific class of an
190    /// object is considered a private implementation detail. Use
191    /// [`isKindOfClass`][Self::isKindOfClass] instead to check whether an
192    /// object is an instance of a given class.
193    ///
194    /// See [Apple's documentation][apple-doc] for more details.
195    ///
196    /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418766-ismemberofclass?language=objc
197    #[doc(alias = "isMemberOfClass:")]
198    fn isMemberOfClass(&self, cls: &AnyClass) -> bool
199    where
200        Self: Sized + Message,
201    {
202        unsafe { msg_send![self, isMemberOfClass: cls] }
203    }
204
205    /// Check whether the object implements or inherits a method with the
206    /// given selector.
207    ///
208    /// See [Apple's documentation][apple-doc] for more details.
209    ///
210    /// If using this for availability checking, you might want to consider
211    /// using the [`available!`] macro instead, as it is often more
212    /// performant than this runtime check.
213    ///
214    /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418583-respondstoselector?language=objc
215    /// [`available!`]: crate::available
216    #[doc(alias = "respondsToSelector:")]
217    fn respondsToSelector(&self, aSelector: Sel) -> bool
218    where
219        Self: Sized + Message,
220    {
221        unsafe { msg_send![self, respondsToSelector: aSelector] }
222    }
223
224    /// Check whether the object conforms to a given protocol.
225    ///
226    /// See [Apple's documentation][apple-doc] for details.
227    ///
228    /// [apple-doc]: https://developer.apple.com/documentation/objectivec/nsobject/1418893-conformstoprotocol?language=objc
229    #[doc(alias = "conformsToProtocol:")]
230    fn conformsToProtocol(&self, aProtocol: &AnyProtocol) -> bool
231    where
232        Self: Sized + Message,
233    {
234        unsafe { msg_send![self, conformsToProtocol: aProtocol] }
235    }
236
237    /// A textual representation of the object.
238    ///
239    /// The returned class is `NSString`, but since that is defined in
240    /// `objc2-foundation`, and `NSObjectProtocol` is defined in `objc2`, the
241    /// declared return type is unfortunately restricted to be [`NSObject`].
242    /// It is always safe to cast the return value of this to `NSString`.
243    ///
244    /// You might want to use the [`Debug`][fmt::Debug] impl of the object
245    /// instead, or if the object implements [`Display`][fmt::Display], the
246    /// [`to_string`][std::string::ToString::to_string] method.
247    ///
248    ///
249    /// # Example
250    ///
251    /// ```
252    /// use objc2::rc::Retained;
253    /// # use objc2::runtime::{NSObjectProtocol, NSObject, NSObject as NSString};
254    /// # #[cfg(available_in_foundation)]
255    /// use objc2_foundation::{NSObject, NSObjectProtocol, NSString};
256    ///
257    /// # let obj = NSObject::new();
258    /// // SAFETY: Descriptions are always `NSString`.
259    /// let desc: Retained<NSString> = unsafe { Retained::cast_unchecked(obj.description()) };
260    /// println!("{desc:?}");
261    /// ```
262    //
263    // Only safe to override if the user-provided return type is NSString.
264    fn description(&self) -> Retained<NSObject>
265    where
266        Self: Sized + Message,
267    {
268        unsafe { msg_send![self, description] }
269    }
270
271    /// A textual representation of the object to use when debugging.
272    ///
273    /// Like with [`description`][Self::description], the return type of this
274    /// is always `NSString`.
275    ///
276    /// LLVM's po command uses this property to create a textual
277    /// representation of the object. The default implementation returns the
278    /// same value as `description`. Override either to provide custom object
279    /// descriptions.
280    // optional, introduced in macOS 10.8
281    //
282    // Only safe to override if the user-provided return type is NSString.
283    fn debugDescription(&self) -> Retained<NSObject>
284    where
285        Self: Sized + Message,
286    {
287        unsafe { msg_send![self, debugDescription] }
288    }
289
290    /// Check whether the receiver is a subclass of the `NSProxy` root class
291    /// instead of the usual [`NSObject`].
292    ///
293    /// See [Apple's documentation][apple-doc] for details.
294    ///
295    /// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418528-isproxy?language=objc
296    ///
297    ///
298    /// # Example
299    ///
300    /// ```
301    /// use objc2::runtime::{NSObject, NSObjectProtocol};
302    ///
303    /// let obj = NSObject::new();
304    /// assert!(!obj.isProxy());
305    /// ```
306    fn isProxy(&self) -> bool
307    where
308        Self: Sized + Message,
309    {
310        unsafe { msg_send![self, isProxy] }
311    }
312
313    /// The reference count of the object.
314    ///
315    /// This can rarely be useful when debugging memory management issues,
316    /// though beware that in most real-world scenarios, your object may be
317    /// retained by several autorelease pools, especially when debug
318    /// assertions are enabled, so this value may not represent what you'd
319    /// expect.
320    ///
321    ///
322    /// # Example
323    ///
324    /// ```
325    /// use objc2::ClassType;
326    /// use objc2::runtime::{NSObject, NSObjectProtocol};
327    ///
328    /// let obj = NSObject::new();
329    /// assert_eq!(obj.retainCount(), 1);
330    /// let obj2 = obj.clone();
331    /// assert_eq!(obj.retainCount(), 2);
332    /// drop(obj2);
333    /// assert_eq!(obj.retainCount(), 1);
334    /// ```
335    fn retainCount(&self) -> NSUInteger
336    where
337        Self: Sized + Message,
338    {
339        unsafe { msg_send![self, retainCount] }
340    }
341
342    // retain, release and autorelease below to this protocol.
343}
344
345// SAFETY: Same as in extern_protocol!
346unsafe impl<T> NSObjectProtocol for ProtocolObject<T> where T: ?Sized + NSObjectProtocol {}
347// SAFETY: Same as in extern_protocol!
348unsafe impl ProtocolType for dyn NSObjectProtocol {
349    const NAME: &'static str = "NSObject";
350    const __INNER: () = ();
351}
352// SAFETY: Same as in extern_protocol!
353unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol
354where
355    T: ?Sized + Message + NSObjectProtocol,
356{
357    const __INNER: () = ();
358}
359
360// SAFETY: Anything that implements `NSObjectProtocol` and is `Send` is valid
361// to convert to `ProtocolObject<dyn NSObjectProtocol + Send>`.
362unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Send
363where
364    T: ?Sized + Message + NSObjectProtocol + Send,
365{
366    const __INNER: () = ();
367}
368
369// SAFETY: Anything that implements `NSObjectProtocol` and is `Sync` is valid
370// to convert to `ProtocolObject<dyn NSObjectProtocol + Sync>`.
371unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Sync
372where
373    T: ?Sized + Message + NSObjectProtocol + Sync,
374{
375    const __INNER: () = ();
376}
377
378// SAFETY: Anything that implements `NSObjectProtocol` and is `Send + Sync` is
379// valid to convert to `ProtocolObject<dyn NSObjectProtocol + Send + Sync>`.
380unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Send + Sync
381where
382    T: ?Sized + Message + NSObjectProtocol + Send + Sync,
383{
384    const __INNER: () = ();
385}
386
387unsafe impl NSObjectProtocol for NSObject {}
388
389#[allow(non_snake_case)] // Follow the naming scheme in framework crates
390impl NSObject {
391    extern_methods!(
392        /// Create a new empty `NSObject`.
393        ///
394        /// This method is a shorthand for calling [`alloc`] and then
395        /// [`init`][Self::init].
396        ///
397        /// [`alloc`]: AllocAnyThread::alloc
398        #[unsafe(method(new))]
399        #[unsafe(method_family = new)]
400        pub fn new() -> Retained<Self>;
401
402        /// Initialize an already allocated object.
403        ///
404        /// See [Apple's documentation][apple-doc] for details.
405        ///
406        /// [apple-doc]: https://developer.apple.com/documentation/objectivec/nsobject/1418641-init?language=objc
407        ///
408        ///
409        /// # Example
410        ///
411        /// ```
412        /// use objc2::runtime::NSObject;
413        /// use objc2::AllocAnyThread;
414        ///
415        /// let obj = NSObject::init(NSObject::alloc());
416        /// ```
417        #[unsafe(method(init))]
418        #[unsafe(method_family = init)]
419        pub fn init(this: Allocated<Self>) -> Retained<Self>;
420
421        #[unsafe(method(doesNotRecognizeSelector:))]
422        #[unsafe(method_family = none)]
423        fn doesNotRecognizeSelector_inner(&self, sel: Sel);
424    );
425
426    /// Handle messages the object doesn’t recognize.
427    ///
428    /// See [Apple's documentation][apple-doc] for details.
429    ///
430    /// [apple-doc]: https://developer.apple.com/documentation/objectivec/nsobject/1418637-doesnotrecognizeselector?language=objc
431    pub fn doesNotRecognizeSelector(&self, sel: Sel) -> ! {
432        self.doesNotRecognizeSelector_inner(sel);
433        unreachable!("doesNotRecognizeSelector: should not return")
434    }
435}
436
437/// Objective-C equality has approximately the same semantics as Rust
438/// equality (although less aptly specified).
439///
440/// At the very least, equality is _expected_ to be symmetric and
441/// transitive, and that's about the best we can do.
442///
443/// See also <https://nshipster.com/equality/>
444impl PartialEq for NSObject {
445    #[inline]
446    #[doc(alias = "isEqual:")]
447    fn eq(&self, other: &Self) -> bool {
448        self.isEqual(Some(other))
449    }
450}
451
452/// Most types' equality is reflexive.
453impl Eq for NSObject {}
454
455/// Hashing in Objective-C has the exact same requirement as in Rust:
456///
457/// > If two objects are equal (as determined by the isEqual: method),
458/// > they must have the same hash value.
459///
460/// See <https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418859-hash>
461impl hash::Hash for NSObject {
462    #[inline]
463    fn hash<H: hash::Hasher>(&self, state: &mut H) {
464        <Self as NSObjectProtocol>::hash(self).hash(state);
465    }
466}
467
468impl fmt::Debug for NSObject {
469    #[inline]
470    #[doc(alias = "description")]
471    #[doc(alias = "debugDescription")]
472    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473        let obj: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(self);
474        obj.fmt(f)
475    }
476}
477
478impl DefaultRetained for NSObject {
479    #[inline]
480    fn default_retained() -> Retained<Self> {
481        Self::new()
482    }
483}
484
485#[cfg(test)]
486mod tests {
487    use super::*;
488    use alloc::format;
489
490    use crate::extern_class;
491    use crate::rc::RcTestObject;
492
493    extern_class!(
494        #[unsafe(super(NSObject))]
495        #[name = "NSObject"]
496        #[derive(Debug, PartialEq, Eq, Hash)]
497        struct FakeSubclass;
498    );
499
500    impl FakeSubclass {
501        fn new() -> Retained<Self> {
502            Retained::downcast(NSObject::new()).unwrap()
503        }
504    }
505
506    #[test]
507    fn test_deref() {
508        let obj: Retained<FakeSubclass> = FakeSubclass::new();
509        let _: &FakeSubclass = &obj;
510        let _: &NSObject = &obj;
511        let _: &AnyObject = &obj;
512    }
513
514    #[test]
515    fn test_as_ref_borrow() {
516        use core::borrow::Borrow;
517
518        fn impls_as_ref_borrow<T: AsRef<U> + Borrow<U> + ?Sized, U: ?Sized>(_: &T) {}
519
520        let obj = FakeSubclass::new();
521        impls_as_ref_borrow::<Retained<FakeSubclass>, FakeSubclass>(&obj);
522        impls_as_ref_borrow::<FakeSubclass, FakeSubclass>(&obj);
523        impls_as_ref_borrow::<NSObject, NSObject>(&obj);
524        impls_as_ref_borrow::<NSObject, AnyObject>(&obj);
525
526        let obj = NSObject::new();
527        impls_as_ref_borrow::<Retained<NSObject>, NSObject>(&obj);
528
529        fn impls_as_ref<T: AsRef<U> + ?Sized, U: ?Sized>(_: &T) {}
530
531        let obj = FakeSubclass::new();
532        impls_as_ref::<Retained<FakeSubclass>, FakeSubclass>(&obj);
533        impls_as_ref::<Retained<FakeSubclass>, NSObject>(&obj);
534        impls_as_ref::<Retained<FakeSubclass>, AnyObject>(&obj);
535    }
536
537    #[test]
538    fn test_equality() {
539        let obj1 = NSObject::new();
540        assert_eq!(obj1, obj1);
541
542        let obj2 = NSObject::new();
543        assert_ne!(obj1, obj2);
544    }
545
546    #[test]
547    fn test_hash() {
548        use core::hash::Hasher;
549        use std::collections::hash_map::DefaultHasher;
550        use std::hash::Hash;
551
552        let obj1 = NSObject::new();
553
554        let mut hashstate1 = DefaultHasher::new();
555        let mut hashstate2 = DefaultHasher::new();
556
557        obj1.hash(&mut hashstate1);
558        obj1.hash(&mut hashstate2);
559
560        assert_eq!(hashstate1.finish(), hashstate2.finish());
561
562        let obj2 = NSObject::new();
563        let mut hashstate2 = DefaultHasher::new();
564        obj2.hash(&mut hashstate2);
565        assert_ne!(hashstate1.finish(), hashstate2.finish());
566    }
567
568    #[test]
569    fn test_debug() {
570        let obj = NSObject::new();
571        let expected = format!("<NSObject: {:p}>", &*obj);
572        assert_eq!(format!("{obj:?}"), expected);
573    }
574
575    #[test]
576    fn test_is_kind_of() {
577        let obj = NSObject::new();
578        assert!(obj.isKindOfClass(NSObject::class()));
579        assert!(!obj.isKindOfClass(RcTestObject::class()));
580
581        let obj = RcTestObject::new();
582        assert!(obj.isKindOfClass(NSObject::class()));
583        assert!(obj.isKindOfClass(RcTestObject::class()));
584    }
585
586    #[test]
587    fn test_retain_same() {
588        let obj1 = NSObject::new();
589        let ptr1 = Retained::as_ptr(&obj1);
590
591        let obj2 = obj1.clone();
592        let ptr2 = Retained::as_ptr(&obj2);
593
594        assert_eq!(ptr1, ptr2);
595    }
596
597    #[test]
598    fn conforms_to_nsobjectprotocol() {
599        let protocol = <dyn NSObjectProtocol>::protocol().unwrap();
600        assert!(NSObject::class().conforms_to(protocol));
601    }
602
603    // Ensure that importing `NSObjectProtocol::hash` does not cause conflicts
604    // when using `Hash::hash` on normal types.
605    mod hash_does_not_overlap_with_normal_hash_method {
606        #[allow(unused_imports)]
607        use crate::runtime::NSObjectProtocol;
608        use std::collections::hash_map::DefaultHasher;
609        use std::hash::Hash;
610
611        #[test]
612        fn inner() {
613            let integer = 5;
614            let mut hasher = DefaultHasher::new();
615            integer.hash(&mut hasher);
616        }
617    }
618}