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