objc2/runtime/mod.rs
1//! # Direct runtime bindings.
2//!
3//! This module contains safe(r) bindings to common parts of the Objective-C
4//! runtime. See the [`ffi`][crate::ffi] module for details on the raw
5//! bindings.
6//!
7//!
8//! # Example
9//!
10//! Using features of the runtime to query information about `NSObject`.
11//!
12//! ```
13#![doc = include_str!("../../examples/introspection.rs")]
14//! ```
15#![allow(clippy::missing_panics_doc)]
16
17use alloc::ffi::CString;
18use alloc::vec::Vec;
19use core::ffi::c_char;
20use core::ffi::c_uint;
21use core::ffi::{c_void, CStr};
22use core::fmt;
23use core::hash;
24use core::panic::{RefUnwindSafe, UnwindSafe};
25use core::ptr::{self, NonNull};
26
27// Note: While this is not public, it is still a breaking change to remove,
28// since `objc2-foundation` relies on it.
29#[doc(hidden)]
30pub mod __nsstring;
31mod bool;
32mod define;
33mod malloc;
34mod message_receiver;
35mod method_encoding_iter;
36mod method_implementation;
37mod nsobject;
38mod nsproxy;
39mod nszone;
40mod protocol_object;
41mod retain_release_fast;
42
43pub(crate) use self::method_encoding_iter::{EncodingParseError, MethodEncodingIter};
44pub(crate) use self::retain_release_fast::{objc_release_fast, objc_retain_fast};
45use crate::encode::{Encode, EncodeArguments, EncodeReturn, Encoding, OptionEncode, RefEncode};
46use crate::msg_send;
47use crate::verify::{verify_method_signature, Inner};
48use crate::{ffi, DowncastTarget, Message};
49
50// Note: While this is not public, it is still a breaking change to remove,
51// since `objc2-foundation` relies on it.
52#[doc(hidden)]
53pub use self::nsproxy::NSProxy as __NSProxy;
54
55pub use self::bool::Bool;
56pub use self::define::{ClassBuilder, ProtocolBuilder};
57pub use self::message_receiver::MessageReceiver;
58pub use self::method_implementation::MethodImplementation;
59pub use self::nsobject::{NSObject, NSObjectProtocol};
60pub use self::nszone::NSZone;
61pub use self::protocol_object::{ImplementedBy, ProtocolObject};
62pub use crate::verify::VerificationError;
63
64#[allow(deprecated)]
65pub use crate::ffi::{BOOL, NO, YES};
66
67use self::malloc::{MallocCStr, MallocSlice};
68
69/// We do not want to expose `MallocSlice` to end users, because in the
70/// future, we want to be able to change it to `Box<[T], MallocAllocator>`.
71///
72/// So instead we use an unnameable type.
73macro_rules! MallocSlice {
74 ($t:ty) => {
75 impl std::ops::Deref<Target = [$t]> + AsRef<[$t]> + std::fmt::Debug
76 };
77}
78
79/// Same as `MallocSlice!`.
80macro_rules! MallocCStr {
81 () => {
82 impl std::ops::Deref<Target = CStr> + AsRef<CStr> + std::fmt::Debug
83 };
84}
85
86/// Implement PartialEq, Eq and Hash using pointer semantics; there's not
87/// really a better way to do it for this type
88macro_rules! standard_pointer_impls {
89 ($name:ident) => {
90 impl PartialEq for $name {
91 #[inline]
92 fn eq(&self, other: &Self) -> bool {
93 ptr::eq(self, other)
94 }
95 }
96 impl Eq for $name {}
97 impl hash::Hash for $name {
98 #[inline]
99 fn hash<H: hash::Hasher>(&self, state: &mut H) {
100 let ptr: *const Self = self;
101 ptr.hash(state)
102 }
103 }
104 };
105}
106
107/// A pointer to the start of a method implementation.
108///
109/// The first argument is a pointer to the receiver, the second argument is
110/// the selector, and the rest of the arguments follow.
111///
112///
113/// # Safety
114///
115/// This is a "catch all" type; it must be transmuted to the correct type
116/// before being called!
117///
118/// Also note that this is non-null! If you require an Imp that can be null,
119/// use `Option<Imp>`.
120#[doc(alias = "IMP")]
121pub type Imp = unsafe extern "C-unwind" fn();
122
123/// A method selector.
124///
125/// The Rust equivalent of Objective-C's `SEL _Nonnull` type. You can create
126/// this statically using the [`sel!`] macro.
127///
128/// The main reason the Objective-C runtime uses a custom type for selectors,
129/// as opposed to a plain c-string, is to support efficient comparison - a
130/// a selector is effectively an [interned string], so this makes equiality
131/// comparisons very cheap.
132///
133/// This struct guarantees the null-pointer optimization, namely that
134/// `Option<Sel>` is the same size as `Sel`.
135///
136/// Selectors are immutable.
137///
138/// [`sel!`]: crate::sel
139/// [interned string]: https://en.wikipedia.org/wiki/String_interning
140#[repr(transparent)]
141#[derive(Copy, Clone)]
142#[doc(alias = "SEL")]
143#[doc(alias = "objc_selector")]
144pub struct Sel {
145 ptr: NonNull<c_void>,
146}
147
148// SAFETY: Sel is immutable (and can be retrieved from any thread using the
149// `sel!` macro).
150unsafe impl Sync for Sel {}
151unsafe impl Send for Sel {}
152impl UnwindSafe for Sel {}
153impl RefUnwindSafe for Sel {}
154
155impl Sel {
156 #[inline]
157 #[doc(hidden)]
158 pub const unsafe fn __internal_from_ptr(ptr: *const u8) -> Self {
159 // Used in static selectors.
160 // SAFETY: Upheld by caller.
161 let ptr = unsafe { NonNull::new_unchecked(ptr as *mut c_void) };
162 Self { ptr }
163 }
164
165 #[inline]
166 pub(crate) unsafe fn from_ptr(ptr: *const c_void) -> Option<Self> {
167 // SAFETY: Caller verifies that the pointer is valid.
168 NonNull::new(ptr as *mut c_void).map(|ptr| Self { ptr })
169 }
170
171 #[inline]
172 pub(crate) const fn as_ptr(&self) -> *const c_void {
173 self.ptr.as_ptr()
174 }
175
176 // We explicitly don't do #[track_caller] here, since we expect the error
177 // to never actually happen.
178 pub(crate) unsafe fn register_unchecked(name: *const c_char) -> Self {
179 let ptr = unsafe { ffi::sel_registerName(name) };
180 // SAFETY: `sel_registerName` declares return type as `SEL _Nonnull`,
181 // at least when input is also `_Nonnull` (which it is in our case).
182 //
183 // Looking at the source code, it can fail and will return NULL if
184 // allocating space for the selector failed (which then subsequently
185 // invokes UB by calling `memcpy` with a NULL argument):
186 // <https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-os.h#L1002-L1004>
187 //
188 // I suspect this will be really uncommon in practice, since the
189 // called selector is almost always going to be present in the binary
190 // already; but alas, we'll handle it!
191 ptr.expect("failed allocating selector")
192 }
193
194 /// Registers a selector with the Objective-C runtime.
195 ///
196 /// This is the dynamic version of the [`sel!`] macro, prefer to use that
197 /// when your selector is static.
198 ///
199 /// [`sel!`]: crate::sel
200 ///
201 ///
202 /// # Panics
203 ///
204 /// Panics if the runtime failed allocating space for the selector.
205 #[inline]
206 #[doc(alias = "sel_registerName")]
207 pub fn register(name: &CStr) -> Self {
208 // SAFETY: Input is a non-null, NUL-terminated C-string pointer.
209 unsafe { Self::register_unchecked(name.as_ptr()) }
210 }
211
212 /// Returns the string representation of the selector.
213 #[inline]
214 #[doc(alias = "sel_getName")]
215 pub fn name(self) -> &'static CStr {
216 // SAFETY: Input is non-null selector. Declares return type as
217 // `const char * _Nonnull`, source code agrees.
218 let ptr = unsafe { ffi::sel_getName(self) };
219 // SAFETY: The string is a valid C-style NUL-terminated string, and
220 // has static lifetime since the selector has static lifetime.
221 unsafe { CStr::from_ptr(ptr) }
222 }
223
224 pub(crate) fn number_of_arguments(self) -> usize {
225 self.name()
226 .to_bytes()
227 .iter()
228 .filter(|&&b| b == b':')
229 .count()
230 }
231}
232
233impl PartialEq for Sel {
234 #[inline]
235 fn eq(&self, other: &Self) -> bool {
236 if cfg!(feature = "gnustep-1-7") {
237 // GNUStep implements "typed" selectors, which means their pointer
238 // values sometimes differ; so let's use the runtime-provided
239 // `sel_isEqual`.
240 unsafe { ffi::sel_isEqual(*self, *other).as_bool() }
241 } else {
242 // `ffi::sel_isEqual` uses pointer comparison on Apple (the
243 // documentation explicitly notes this); so as an optimization,
244 // let's do that as well!
245 ptr::eq(self.as_ptr(), other.as_ptr())
246 }
247 }
248}
249
250impl Eq for Sel {}
251
252impl hash::Hash for Sel {
253 #[inline]
254 fn hash<H: hash::Hasher>(&self, state: &mut H) {
255 if cfg!(feature = "gnustep-1-7") {
256 // Note: We hash the name instead of the pointer on GNUStep, since
257 // they're typed.
258 self.name().hash(state);
259 } else {
260 self.as_ptr().hash(state);
261 }
262 }
263}
264
265// SAFETY: `Sel` is FFI compatible, and the encoding is `Sel`.
266unsafe impl Encode for Sel {
267 const ENCODING: Encoding = Encoding::Sel;
268}
269
270unsafe impl OptionEncode for Sel {}
271
272// RefEncode is not implemented for Sel, because there is literally no API
273// that takes &Sel, while the user could get confused and accidentally attempt
274// that.
275
276impl fmt::Display for Sel {
277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278 // Selectors are basically always UTF-8, so it's _fine_ to do a lossy
279 // conversion here.
280 fmt::Display::fmt(&self.name().to_string_lossy(), f)
281 }
282}
283
284impl fmt::Debug for Sel {
285 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286 f.debug_tuple("Sel").field(&self.name()).finish()
287 }
288}
289
290impl fmt::Pointer for Sel {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 fmt::Pointer::fmt(&self.ptr, f)
293 }
294}
295
296/// An opaque type that represents an instance variable.
297///
298/// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/ivar?language=objc).
299#[repr(C)]
300#[doc(alias = "objc_ivar")]
301pub struct Ivar {
302 _priv: [u8; 0],
303 _p: ffi::OpaqueData,
304}
305
306// SAFETY: Ivar is immutable (and can be retrieved from AnyClass anyhow).
307unsafe impl Sync for Ivar {}
308unsafe impl Send for Ivar {}
309impl UnwindSafe for Ivar {}
310impl RefUnwindSafe for Ivar {}
311
312impl Ivar {
313 /// Returns the instance variable's name.
314 ///
315 /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418922-ivar_getname?language=objc).
316 #[inline]
317 #[doc(alias = "ivar_getName")]
318 pub fn name(&self) -> &CStr {
319 unsafe { CStr::from_ptr(ffi::ivar_getName(self)) }
320 }
321
322 /// Returns the instance variable's offset from the object base.
323 ///
324 /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418976-ivar_getoffset?language=objc).
325 #[inline]
326 #[doc(alias = "ivar_getOffset")]
327 pub fn offset(&self) -> isize {
328 unsafe { ffi::ivar_getOffset(self) }
329 }
330
331 /// Returns the instance variable's `@encode(type)` string.
332 ///
333 /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418569-ivar_gettypeencoding?language=objc).
334 #[inline]
335 #[doc(alias = "ivar_getTypeEncoding")]
336 pub fn type_encoding(&self) -> &CStr {
337 unsafe { CStr::from_ptr(ffi::ivar_getTypeEncoding(self)) }
338 }
339
340 #[inline]
341 pub(crate) fn debug_assert_encoding(&self, _expected: &Encoding) {
342 #[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
343 {
344 let encoding = self.type_encoding();
345 let encoding = encoding.to_str().expect("encoding must be UTF-8");
346 assert!(
347 _expected.equivalent_to_str(encoding),
348 "wrong encoding. Tried to retrieve ivar with encoding {encoding}, but the encoding of the given type was {_expected}",
349 );
350 }
351 }
352
353 /// Returns a pointer to the instance variable / ivar on the given object.
354 ///
355 /// This is similar to [`UnsafeCell::get`], see that for more information
356 /// on what is and isn't safe to do.
357 ///
358 /// Usually you will have defined the instance variable yourself with
359 /// [`ClassBuilder::add_ivar`], the type of the ivar `T` must match the
360 /// type used in that.
361 ///
362 /// Library implementors are strongly encouraged to expose a safe
363 /// interface to the ivar.
364 ///
365 /// [`UnsafeCell::get`]: core::cell::UnsafeCell::get
366 /// [`ClassBuilder::add_ivar`]: crate::runtime::ClassBuilder::add_ivar
367 ///
368 ///
369 /// # Panics
370 ///
371 /// Panics when `debug_assertions` are enabled if the type encoding of the
372 /// ivar differs from the type encoding of `T`. This can be disabled with
373 /// the `"disable-encoding-assertions"` Cargo feature flag.
374 ///
375 ///
376 /// # Safety
377 ///
378 /// The object must have the given instance variable on it, and it must be
379 /// of type `T`. Any invariants that the object have assumed about the
380 /// value of the instance variable must not be violated.
381 ///
382 /// Note that an object can have multiple instance variables with the same
383 /// name; you must ensure that when the instance variable was retrieved,
384 /// was retrieved from the class that it was defined on. In particular,
385 /// getting a class dynamically using e.g. [`AnyObject::class`], and using
386 /// an instance variable from that here is _not_ sound in general.
387 ///
388 /// No thread synchronization is done on accesses to the variable, so you
389 /// must ensure that any access to the returned pointer do not cause data
390 /// races, and that Rust's mutability rules are not otherwise violated.
391 #[inline]
392 pub unsafe fn load_ptr<T: Encode>(&self, obj: &AnyObject) -> *mut T {
393 self.debug_assert_encoding(&T::ENCODING);
394
395 let ptr = NonNull::from(obj);
396 // SAFETY: That the ivar is valid is ensured by the caller
397 let ptr = unsafe { AnyObject::ivar_at_offset::<T>(ptr, self.offset()) };
398
399 // Safe as *mut T because `self` is `UnsafeCell`
400 ptr.as_ptr()
401 }
402
403 /// Returns a reference to the instance variable with the given name.
404 ///
405 /// See [`Ivar::load_ptr`] for more information.
406 ///
407 ///
408 /// # Panics
409 ///
410 /// Panics when `debug_assertions` are enabled if the type encoding of the
411 /// ivar differs from the type encoding of `T`. This can be disabled with
412 /// the `"disable-encoding-assertions"` Cargo feature flag.
413 ///
414 ///
415 /// # Safety
416 ///
417 /// The object must have the given instance variable on it, and it must be
418 /// of type `T`.
419 ///
420 /// No thread synchronization is done, so you must ensure that no other
421 /// thread is concurrently mutating the variable. This requirement can be
422 /// considered upheld if all mutation happens through [`Ivar::load_mut`]
423 /// (since that takes the object mutably).
424 #[inline]
425 pub unsafe fn load<'obj, T: Encode>(&self, obj: &'obj AnyObject) -> &'obj T {
426 // SAFETY: That the ivar is valid as `&T` is ensured by the caller,
427 // and the reference is properly bound to the object.
428 unsafe { self.load_ptr::<T>(obj).as_ref().unwrap_unchecked() }
429 }
430
431 /// Returns a mutable reference to the ivar with the given name.
432 ///
433 /// See [`Ivar::load_ptr`] for more information.
434 ///
435 ///
436 /// # Panics
437 ///
438 /// Panics when `debug_assertions` are enabled if the type encoding of the
439 /// ivar differs from the type encoding of `T`. This can be disabled with
440 /// the `"disable-encoding-assertions"` Cargo feature flag.
441 ///
442 ///
443 /// # Safety
444 ///
445 /// The object must have an instance variable with the given name, and it
446 /// must be of type `T`.
447 ///
448 /// This access happens through `&mut`, which means we know it to be the
449 /// only reference, hence you do not need to do any work to ensure that
450 /// data races do not happen.
451 #[inline]
452 pub unsafe fn load_mut<'obj, T: Encode>(&self, obj: &'obj mut AnyObject) -> &'obj mut T {
453 self.debug_assert_encoding(&T::ENCODING);
454
455 let ptr = NonNull::from(obj);
456 // SAFETY: That the ivar is valid is ensured by the caller
457 let mut ptr = unsafe { AnyObject::ivar_at_offset::<T>(ptr, self.offset()) };
458
459 // SAFETY: That the ivar is valid as `&mut T` is ensured by taking an
460 // `&mut` object
461 unsafe { ptr.as_mut() }
462 }
463}
464
465standard_pointer_impls!(Ivar);
466
467impl fmt::Debug for Ivar {
468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469 f.debug_struct("Ivar")
470 .field("name", &self.name())
471 .field("offset", &self.offset())
472 .field("type_encoding", &self.type_encoding())
473 .finish_non_exhaustive()
474 }
475}
476
477#[derive(Debug, PartialEq, Eq, Hash)]
478pub(crate) struct MethodDescription {
479 pub(crate) sel: Sel,
480 pub(crate) types: &'static CStr,
481}
482
483impl MethodDescription {
484 pub(crate) unsafe fn from_raw(raw: ffi::objc_method_description) -> Option<Self> {
485 let sel = raw.name?;
486 if raw.types.is_null() {
487 return None;
488 }
489 // SAFETY: We've checked that the pointer is not NULL, rest is checked
490 // by caller.
491 let types = unsafe { CStr::from_ptr(raw.types) };
492 Some(Self { sel, types })
493 }
494}
495
496/// A type that represents a method in a class definition.
497///
498/// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/method?language=objc).
499#[repr(C)]
500#[doc(alias = "objc_method")]
501pub struct Method {
502 _priv: [u8; 0],
503 _p: ffi::OpaqueData,
504}
505
506// SAFETY: Method is immutable (and can be retrieved from AnyClass anyhow).
507unsafe impl Sync for Method {}
508unsafe impl Send for Method {}
509impl UnwindSafe for Method {}
510impl RefUnwindSafe for Method {}
511
512impl Method {
513 // Note: We don't take `&mut` here, since the operations on methods work
514 // atomically.
515 #[inline]
516 fn as_mut_ptr(&self) -> *mut Self {
517 let ptr: *const Self = self;
518 ptr as _
519 }
520
521 /// Returns the name of self.
522 #[inline]
523 #[doc(alias = "method_getName")]
524 pub fn name(&self) -> Sel {
525 unsafe { ffi::method_getName(self).unwrap() }
526 }
527
528 /// Returns the `Encoding` of self's return type.
529 #[doc(alias = "method_copyReturnType")]
530 pub fn return_type(&self) -> MallocCStr!() {
531 unsafe {
532 let encoding = ffi::method_copyReturnType(self);
533 MallocCStr::from_c_str(encoding)
534 }
535 }
536
537 /// Returns the `Encoding` of a single parameter type of self, or
538 /// [`None`] if self has no parameter at the given index.
539 #[doc(alias = "method_copyArgumentType")]
540 pub fn argument_type(&self, index: usize) -> Option<MallocCStr!()> {
541 unsafe {
542 let encoding = ffi::method_copyArgumentType(self, index as c_uint);
543 NonNull::new(encoding).map(|encoding| MallocCStr::from_c_str(encoding.as_ptr()))
544 }
545 }
546
547 /// An iterator over the method's types.
548 ///
549 /// It is approximately equivalent to:
550 ///
551 /// ```ignore
552 /// let types = method.types();
553 /// assert_eq!(types.next()?, method.return_type());
554 /// for i in 0..method.arguments_count() {
555 /// assert_eq!(types.next()?, method.argument_type(i)?);
556 /// }
557 /// assert!(types.next().is_none());
558 /// ```
559 #[doc(alias = "method_getTypeEncoding")]
560 pub(crate) fn types(&self) -> MethodEncodingIter<'_> {
561 // SAFETY: The method pointer is valid and non-null
562 let cstr = unsafe { ffi::method_getTypeEncoding(self) };
563 if cstr.is_null() {
564 panic!("method type encoding was NULL");
565 }
566 // SAFETY: `method_getTypeEncoding` returns a C-string, and we just
567 // checked that it is non-null.
568 let encoding = unsafe { CStr::from_ptr(cstr) };
569 let s = encoding
570 .to_str()
571 .expect("method type encoding must be UTF-8");
572 MethodEncodingIter::new(s)
573 }
574
575 /// Returns the number of arguments accepted by self.
576 #[inline]
577 #[doc(alias = "method_getNumberOfArguments")]
578 pub fn arguments_count(&self) -> usize {
579 unsafe { ffi::method_getNumberOfArguments(self) as usize }
580 }
581
582 /// Returns the implementation of this method.
583 #[doc(alias = "method_getImplementation")]
584 pub fn implementation(&self) -> Imp {
585 unsafe { ffi::method_getImplementation(self).expect("null IMP") }
586 }
587
588 /// Set the implementation of this method.
589 ///
590 /// Note that any thread may at any point be changing method
591 /// implementations, so if you intend to call the previous method as
592 /// returned by e.g. [`Self::implementation`], beware that that may now be
593 /// stale.
594 ///
595 /// The previous implementation is returned from this function though, so
596 /// you can call that instead.
597 ///
598 /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418707-method_setimplementation?language=objc).
599 ///
600 ///
601 /// # Safety
602 ///
603 /// The given implementation function pointer must:
604 ///
605 /// 1. Have the signature expected by the Objective-C runtime and callers
606 /// of this method.
607 ///
608 /// 2. Be at least as safe as the existing method, i.e. by overriding the
609 /// previous method, it should not be possible for the program to cause
610 /// UB.
611 ///
612 /// A common mistake would be expecting e.g. a pointer to not be null,
613 /// where the null case was handled before.
614 #[doc(alias = "method_setImplementation")]
615 pub unsafe fn set_implementation(&self, imp: Imp) -> Imp {
616 // SAFETY: The new impl is not NULL, and the rest is upheld by the
617 // caller.
618 unsafe { ffi::method_setImplementation(self.as_mut_ptr(), imp).expect("null IMP") }
619 }
620
621 /// Exchange the implementation of two methods.
622 ///
623 /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418769-method_exchangeimplementations?language=objc).
624 ///
625 ///
626 /// # Safety
627 ///
628 /// The two methods must be perfectly compatible, both in signature, and
629 /// in expected (in terms of safety, not necessarily behaviour) input and
630 /// output.
631 ///
632 ///
633 /// # Example
634 ///
635 /// This is an atomic version of the following:
636 ///
637 /// ```
638 /// use objc2::runtime::Method;
639 /// # use objc2::runtime::NSObject;
640 /// # use objc2::sel;
641 /// # use crate::objc2::ClassType;
642 ///
643 /// let m1: &Method;
644 /// let m2: &Method;
645 /// #
646 /// # // Use the same method twice, to avoid actually changing anything
647 /// # m1 = NSObject::class().instance_method(sel!(hash)).unwrap();
648 /// # m2 = NSObject::class().instance_method(sel!(hash)).unwrap();
649 ///
650 /// unsafe {
651 /// let imp = m2.set_implementation(m1.implementation());
652 /// m1.set_implementation(imp);
653 /// }
654 /// ```
655 #[inline]
656 #[doc(alias = "method_exchangeImplementations")]
657 pub unsafe fn exchange_implementation(&self, other: &Self) {
658 // TODO: Consider checking that `self.types()` and `other.types()`
659 // match when debug assertions are enabled?
660
661 // SAFETY: Verified by caller
662 unsafe { ffi::method_exchangeImplementations(self.as_mut_ptr(), other.as_mut_ptr()) }
663 }
664}
665
666standard_pointer_impls!(Method);
667
668impl fmt::Debug for Method {
669 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
670 f.debug_struct("Method")
671 .field("name", &self.name())
672 .field("types", &self.types())
673 .field("implementation", &self.implementation())
674 .finish_non_exhaustive()
675 }
676}
677
678/// An opaque type that represents an Objective-C class.
679///
680/// This is an opaque type meant to be used behind a shared reference
681/// `&AnyClass`, which is semantically equivalent to `Class _Nonnull`.
682///
683/// A nullable class can be used as `Option<&AnyClass>`.
684///
685/// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/class?language=objc).
686#[repr(C)]
687#[doc(alias = "Class")]
688#[doc(alias = "objc_class")]
689pub struct AnyClass {
690 // `isa` field is deprecated and not available on GNUStep, so we don't
691 // expose it here. Use `class_getSuperclass` instead.
692 inner: AnyObject,
693}
694
695/// Use [`AnyClass`] instead.
696#[deprecated = "renamed to `runtime::AnyClass`"]
697pub type Class = AnyClass;
698
699// SAFETY: AnyClass is immutable (and can be retrieved from any thread using
700// the `class!` macro).
701unsafe impl Sync for AnyClass {}
702unsafe impl Send for AnyClass {}
703impl UnwindSafe for AnyClass {}
704impl RefUnwindSafe for AnyClass {}
705// Note that Unpin is not applicable.
706
707impl AnyClass {
708 /// Returns the class definition of a specified class, or [`None`] if the
709 /// class is not registered with the Objective-C runtime.
710 #[inline]
711 #[doc(alias = "objc_getClass")]
712 pub fn get(name: &CStr) -> Option<&'static Self> {
713 let cls = unsafe { ffi::objc_getClass(name.as_ptr()) };
714 unsafe { cls.as_ref() }
715 }
716
717 // Same as `get`, but ...
718 // fn lookup(name: &CStr) -> Option<&'static Self>;
719
720 /// Obtains the list of registered class definitions.
721 #[doc(alias = "objc_copyClassList")]
722 pub fn classes() -> MallocSlice!(&'static Self) {
723 unsafe {
724 let mut count: c_uint = 0;
725 let classes: *mut &Self = ffi::objc_copyClassList(&mut count).cast();
726 MallocSlice::from_array(classes, count as usize)
727 }
728 }
729
730 /// Returns the total number of registered classes.
731 #[inline]
732 #[doc(alias = "objc_getClassList")]
733 pub fn classes_count() -> usize {
734 unsafe { ffi::objc_getClassList(ptr::null_mut(), 0) as usize }
735 }
736
737 /// # Safety
738 ///
739 /// 1. The class pointer must be valid.
740 /// 2. The string is unbounded, so the caller must bound it.
741 pub(crate) unsafe fn name_raw<'a>(ptr: *const Self) -> &'a CStr {
742 // SAFETY: Caller ensures that the pointer is valid
743 let name = unsafe { ffi::class_getName(ptr) };
744 if name.is_null() {
745 panic!("class name was NULL");
746 }
747 // SAFETY: We've checked that the pointer is not NULL, and
748 // `class_getName` is guaranteed to return a valid C-string.
749 //
750 // That the result is properly bounded is checked by the caller.
751 unsafe { CStr::from_ptr(name) }
752 }
753
754 /// Returns the name of the class.
755 #[inline]
756 #[doc(alias = "class_getName")]
757 pub fn name(&self) -> &CStr {
758 // SAFETY: The pointer is valid, and the return is properly bounded
759 unsafe { Self::name_raw(self) }
760 }
761
762 /// # Safety
763 ///
764 /// 1. The class pointer must be valid.
765 /// 2. The caller must bound the lifetime of the returned class.
766 #[inline]
767 pub(crate) unsafe fn superclass_raw<'a>(ptr: *const Self) -> Option<&'a AnyClass> {
768 // SAFETY: Caller ensures that the pointer is valid
769 let superclass = unsafe { ffi::class_getSuperclass(ptr) };
770 // SAFETY: The result is properly bounded by the caller.
771 unsafe { superclass.as_ref() }
772 }
773
774 /// Returns the superclass of self, or [`None`] if self is a root class.
775 #[inline]
776 #[doc(alias = "class_getSuperclass")]
777 pub fn superclass(&self) -> Option<&AnyClass> {
778 // SAFETY: The pointer is valid, and the return is properly bounded
779 unsafe { Self::superclass_raw(self) }
780 }
781
782 /// Returns the metaclass of self.
783 ///
784 ///
785 /// # Example
786 ///
787 /// Get the metaclass of an object.
788 ///
789 /// ```
790 /// use objc2::runtime::NSObject;
791 /// use objc2::ClassType;
792 ///
793 /// let cls = NSObject::class();
794 /// let metacls = cls.metaclass();
795 ///
796 /// assert_eq!(metacls.name(), c"NSObject");
797 /// ```
798 #[inline]
799 #[doc(alias = "object_getClass")]
800 #[doc(alias = "objc_getMetaClass")] // Same as `AnyClass::get(name).metaclass()`
801 pub fn metaclass(&self) -> &Self {
802 let ptr: *const Self = self;
803 let ptr = unsafe { ffi::object_getClass(ptr.cast()) };
804 unsafe { ptr.as_ref().unwrap_unchecked() }
805 }
806
807 /// Whether the class is a metaclass.
808 ///
809 ///
810 /// # Example
811 ///
812 /// ```
813 /// use objc2::runtime::NSObject;
814 /// use objc2::ClassType;
815 ///
816 /// let cls = NSObject::class();
817 /// let metacls = cls.metaclass();
818 ///
819 /// assert!(!cls.is_metaclass());
820 /// assert!(metacls.is_metaclass());
821 /// ```
822 #[inline]
823 #[doc(alias = "class_isMetaClass")]
824 pub fn is_metaclass(&self) -> bool {
825 unsafe { ffi::class_isMetaClass(self).as_bool() }
826 }
827
828 /// Returns the size of instances of self.
829 #[inline]
830 #[doc(alias = "class_getInstanceSize")]
831 pub fn instance_size(&self) -> usize {
832 unsafe { ffi::class_getInstanceSize(self) }
833 }
834
835 /// Returns a specified instance method for self, or [`None`] if self and
836 /// its superclasses do not contain an instance method with the specified
837 /// selector.
838 #[inline]
839 #[doc(alias = "class_getInstanceMethod")]
840 pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
841 unsafe {
842 let method = ffi::class_getInstanceMethod(self, sel);
843 method.as_ref()
844 }
845 }
846
847 /// Returns a specified class method for self, or [`None`] if self and
848 /// its superclasses do not contain a class method with the specified
849 /// selector.
850 ///
851 /// Same as `cls.metaclass().class_method()`.
852 #[inline]
853 #[doc(alias = "class_getClassMethod")]
854 pub fn class_method(&self, sel: Sel) -> Option<&Method> {
855 unsafe {
856 let method = ffi::class_getClassMethod(self, sel);
857 method.as_ref()
858 }
859 }
860
861 /// Returns the ivar for a specified instance variable of self, or
862 /// [`None`] if self has no ivar with the given name.
863 ///
864 /// If the instance variable was not found on the specified class, the
865 /// superclasses are searched.
866 ///
867 /// Attempting to access or modify instance variables of a class that you
868 /// do no control may invoke undefined behaviour.
869 #[inline]
870 #[doc(alias = "class_getInstanceVariable")]
871 pub fn instance_variable(&self, name: &CStr) -> Option<&Ivar> {
872 unsafe {
873 let ivar = ffi::class_getInstanceVariable(self, name.as_ptr());
874 ivar.as_ref()
875 }
876 }
877
878 #[allow(unused)]
879 #[inline]
880 #[doc(alias = "class_getClassVariable")]
881 fn class_variable(&self, name: &CStr) -> Option<&Ivar> {
882 let ivar = unsafe { ffi::class_getClassVariable(self, name.as_ptr()) };
883 // SAFETY: TODO
884 unsafe { ivar.as_ref() }
885 }
886
887 /// Describes the instance methods implemented by self.
888 #[doc(alias = "class_copyMethodList")]
889 pub fn instance_methods(&self) -> MallocSlice!(&Method) {
890 unsafe {
891 let mut count: c_uint = 0;
892 let methods: *mut &Method = ffi::class_copyMethodList(self, &mut count).cast();
893 MallocSlice::from_array(methods, count as usize)
894 }
895 }
896
897 /// Checks whether this class conforms to the specified protocol.
898 #[inline]
899 #[doc(alias = "class_conformsToProtocol")]
900 pub fn conforms_to(&self, proto: &AnyProtocol) -> bool {
901 unsafe { ffi::class_conformsToProtocol(self, proto).as_bool() }
902 }
903
904 /// Get a list of the protocols to which this class conforms.
905 #[doc(alias = "class_copyProtocolList")]
906 pub fn adopted_protocols(&self) -> MallocSlice!(&AnyProtocol) {
907 unsafe {
908 let mut count: c_uint = 0;
909 let protos: *mut &AnyProtocol = ffi::class_copyProtocolList(self, &mut count).cast();
910 MallocSlice::from_array(protos, count as usize)
911 }
912 }
913
914 /// Get a list of instance variables on the class.
915 #[doc(alias = "class_copyIvarList")]
916 pub fn instance_variables(&self) -> MallocSlice!(&Ivar) {
917 unsafe {
918 let mut count: c_uint = 0;
919 let ivars: *mut &Ivar = ffi::class_copyIvarList(self, &mut count).cast();
920 MallocSlice::from_array(ivars, count as usize)
921 }
922 }
923
924 /// Check whether instances of this class respond to the given selector.
925 ///
926 /// This doesn't call `respondsToSelector:`, but works entirely within the
927 /// runtime, which means it'll always be safe to call, but may not return
928 /// exactly what you'd expect if `respondsToSelector:` has been
929 /// overwritten.
930 ///
931 /// That said, it will always return `true` if an instance of the class
932 /// responds to the selector, but may return `false` if they don't
933 /// directly (e.g. does so by using forwarding instead).
934 #[inline]
935 #[doc(alias = "class_respondsToSelector")]
936 pub fn responds_to(&self, sel: Sel) -> bool {
937 // This may call `resolveInstanceMethod:` and `resolveClassMethod:`
938 // SAFETY: The selector is guaranteed non-null.
939 unsafe { ffi::class_respondsToSelector(self, sel).as_bool() }
940 }
941
942 // <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html>
943 // fn property(&self, name: &CStr) -> Option<&Property>;
944 // fn properties(&self) -> MallocSlice!(&Property);
945 // unsafe fn replace_method(&self, name: Sel, imp: Imp, types: &CStr) -> Imp;
946 // unsafe fn replace_property(&self, name: &CStr, attributes: &[ffi::objc_property_attribute_t]);
947 // fn method_imp(&self, name: Sel) -> Imp; // + _stret
948
949 // fn get_version(&self) -> u32;
950 // unsafe fn set_version(&mut self, version: u32);
951
952 /// Verify argument and return types for a given selector.
953 ///
954 /// This will look up the encoding of the method for the given selector
955 /// and return a [`VerificationError`] if any encodings differ for the
956 /// arguments `A` and return type `R`.
957 ///
958 ///
959 /// # Example
960 ///
961 /// ```
962 /// use objc2::{class, sel};
963 /// use objc2::runtime::{AnyClass, Bool};
964 /// let cls = class!(NSObject);
965 /// let sel = sel!(isKindOfClass:);
966 /// // Verify that `isKindOfClass:`:
967 /// // - Exists on the class
968 /// // - Takes a class as a parameter
969 /// // - Returns a BOOL
970 /// let result = cls.verify_sel::<(&AnyClass,), Bool>(sel);
971 /// assert!(result.is_ok());
972 /// ```
973 #[allow(clippy::missing_errors_doc)] // Written differently in the docs
974 pub fn verify_sel<A, R>(&self, sel: Sel) -> Result<(), VerificationError>
975 where
976 A: EncodeArguments,
977 R: EncodeReturn,
978 {
979 let method = self.instance_method(sel).ok_or(Inner::MethodNotFound)?;
980 verify_method_signature(method, A::ENCODINGS, &R::ENCODING_RETURN)
981 }
982}
983
984standard_pointer_impls!(AnyClass);
985
986unsafe impl RefEncode for AnyClass {
987 const ENCODING_REF: Encoding = Encoding::Class;
988}
989
990// SAFETY: Classes act as objects, and can be sent messages.
991unsafe impl Message for AnyClass {}
992
993impl fmt::Debug for AnyClass {
994 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
995 f.debug_struct("AnyClass")
996 .field("name", &self.name())
997 .finish_non_exhaustive()
998 }
999}
1000
1001impl fmt::Display for AnyClass {
1002 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1003 // Classes are usually UTF-8, so it's probably fine to do a lossy
1004 // conversion here.
1005 fmt::Display::fmt(&self.name().to_string_lossy(), f)
1006 }
1007}
1008
1009impl AsRef<Self> for AnyClass {
1010 fn as_ref(&self) -> &Self {
1011 self
1012 }
1013}
1014
1015// This is the same as what Swift allows (`AnyClass` coerces to `AnyObject`).
1016impl AsRef<AnyObject> for AnyClass {
1017 fn as_ref(&self) -> &AnyObject {
1018 &self.inner
1019 }
1020}
1021
1022/// An opaque type that represents a protocol in the Objective-C runtime.
1023///
1024/// See [`ProtocolObject`] for objects that implement a specific protocol.
1025//
1026// The naming of this follows GNUStep; this struct does not exist in Apple's
1027// runtime, there `Protocol` is a type alias of `objc_object`.
1028#[repr(C)]
1029#[doc(alias = "objc_protocol")]
1030pub struct AnyProtocol {
1031 inner: AnyObject,
1032}
1033
1034/// Use [`AnyProtocol`] instead.
1035#[deprecated = "renamed to `runtime::AnyProtocol`"]
1036pub type Protocol = AnyProtocol;
1037
1038// SAFETY: AnyProtocol is immutable (and can be retrieved from AnyClass anyhow).
1039unsafe impl Sync for AnyProtocol {}
1040unsafe impl Send for AnyProtocol {}
1041impl UnwindSafe for AnyProtocol {}
1042impl RefUnwindSafe for AnyProtocol {}
1043// Note that Unpin is not applicable.
1044
1045impl AnyProtocol {
1046 /// Returns the protocol definition of a specified protocol, or [`None`]
1047 /// if the protocol is not registered with the Objective-C runtime.
1048 #[inline]
1049 #[doc(alias = "objc_getProtocol")]
1050 pub fn get(name: &CStr) -> Option<&'static Self> {
1051 unsafe {
1052 let proto = ffi::objc_getProtocol(name.as_ptr());
1053 proto.cast::<Self>().as_ref()
1054 }
1055 }
1056
1057 /// Obtains the list of registered protocol definitions.
1058 #[doc(alias = "objc_copyProtocolList")]
1059 pub fn protocols() -> MallocSlice!(&'static Self) {
1060 unsafe {
1061 let mut count: c_uint = 0;
1062 let protocols: *mut &Self = ffi::objc_copyProtocolList(&mut count).cast();
1063 MallocSlice::from_array(protocols, count as usize)
1064 }
1065 }
1066
1067 /// Get a list of the protocols to which this protocol conforms.
1068 #[doc(alias = "protocol_copyProtocolList")]
1069 pub fn adopted_protocols(&self) -> MallocSlice!(&AnyProtocol) {
1070 unsafe {
1071 let mut count: c_uint = 0;
1072 let protocols: *mut &AnyProtocol =
1073 ffi::protocol_copyProtocolList(self, &mut count).cast();
1074 MallocSlice::from_array(protocols, count as usize)
1075 }
1076 }
1077
1078 /// Checks whether this protocol conforms to the specified protocol.
1079 #[inline]
1080 #[doc(alias = "protocol_conformsToProtocol")]
1081 pub fn conforms_to(&self, proto: &AnyProtocol) -> bool {
1082 unsafe { ffi::protocol_conformsToProtocol(self, proto).as_bool() }
1083 }
1084
1085 /// Returns the name of self.
1086 #[inline]
1087 #[doc(alias = "protocol_getName")]
1088 pub fn name(&self) -> &CStr {
1089 unsafe { CStr::from_ptr(ffi::protocol_getName(self)) }
1090 }
1091
1092 fn method_descriptions_inner(&self, required: bool, instance: bool) -> Vec<MethodDescription> {
1093 let mut count: c_uint = 0;
1094 let descriptions = unsafe {
1095 ffi::protocol_copyMethodDescriptionList(
1096 self,
1097 Bool::new(required),
1098 Bool::new(instance),
1099 &mut count,
1100 )
1101 };
1102 if descriptions.is_null() {
1103 return Vec::new();
1104 }
1105 let descriptions = unsafe { MallocSlice::from_array(descriptions, count as usize) };
1106 descriptions
1107 .iter()
1108 .map(|desc| {
1109 unsafe { MethodDescription::from_raw(*desc) }.expect("invalid method description")
1110 })
1111 .collect()
1112 }
1113
1114 #[allow(dead_code)]
1115 #[doc(alias = "protocol_copyMethodDescriptionList")]
1116 pub(crate) fn method_descriptions(&self, required: bool) -> Vec<MethodDescription> {
1117 self.method_descriptions_inner(required, true)
1118 }
1119
1120 #[allow(dead_code)]
1121 #[doc(alias = "protocol_copyMethodDescriptionList")]
1122 pub(crate) fn class_method_descriptions(&self, required: bool) -> Vec<MethodDescription> {
1123 self.method_descriptions_inner(required, false)
1124 }
1125}
1126
1127impl PartialEq for AnyProtocol {
1128 /// Check whether the protocols are equal, or conform to each other.
1129 #[inline]
1130 #[doc(alias = "protocol_isEqual")]
1131 fn eq(&self, other: &Self) -> bool {
1132 unsafe { ffi::protocol_isEqual(self, other).as_bool() }
1133 }
1134}
1135
1136impl Eq for AnyProtocol {}
1137
1138// Don't implement `Hash` for protocol, it is unclear how that would work
1139
1140unsafe impl RefEncode for AnyProtocol {
1141 // Protocols are objects internally.
1142 const ENCODING_REF: Encoding = Encoding::Object;
1143}
1144
1145/// Note that protocols are objects, though sending messages to them is
1146/// officially deprecated.
1147//
1148// SAFETY: Protocols are NSObjects internally (and somewhat publicly, see e.g.
1149// `objc/Protocol.h`), and are returned as `Retained` in various places in
1150// Foundation. But that's considered deprecated, so we don't implement
1151// ClassType for them (even though the "Protocol" class exists).
1152unsafe impl Message for AnyProtocol {}
1153
1154impl fmt::Debug for AnyProtocol {
1155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1156 f.debug_struct("AnyProtocol")
1157 .field("name", &self.name())
1158 .finish_non_exhaustive()
1159 }
1160}
1161
1162impl fmt::Display for AnyProtocol {
1163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1164 // Protocols are usually UTF-8, so it's probably fine to do a lossy
1165 // conversion here.
1166 fmt::Display::fmt(&self.name().to_string_lossy(), f)
1167 }
1168}
1169
1170impl AsRef<Self> for AnyProtocol {
1171 fn as_ref(&self) -> &Self {
1172 self
1173 }
1174}
1175
1176impl AsRef<AnyObject> for AnyProtocol {
1177 fn as_ref(&self) -> &AnyObject {
1178 &self.inner
1179 }
1180}
1181
1182/// An Objective-C object.
1183///
1184/// This is slightly different from [`NSObject`] in that it may represent an
1185/// instance of an _arbitrary_ Objective-C class (e.g. it does not have to be
1186/// a subclass of `NSObject`, so it can represent other root classes like
1187/// `NSProxy`).
1188///
1189/// `Retained<AnyObject>` is equivalent to Objective-C's `id _Nonnull`.
1190///
1191/// This is an opaque type that contains [`UnsafeCell`], and is similar to
1192/// that in that one can safely access and perform interior mutability on this
1193/// (both via [`msg_send!`] and through ivars), so long as Rust's mutability
1194/// rules are upheld, and that data races are avoided.
1195///
1196/// Note: This is intentionally neither [`Sync`], [`Send`], [`UnwindSafe`],
1197/// [`RefUnwindSafe`] nor [`Unpin`], since that is something that may change
1198/// depending on the specific subclass. For example, `NSAutoreleasePool` is
1199/// not `Send`, it has to be deallocated on the same thread that it was
1200/// created. `NSLock` is not `Send` either.
1201///
1202/// [`UnsafeCell`]: core::cell::UnsafeCell
1203/// [`msg_send!`]: crate::msg_send
1204#[doc(alias = "id")]
1205#[doc(alias = "objc_object")]
1206#[repr(C)]
1207pub struct AnyObject {
1208 // `isa` field is deprecated, so we don't expose it here.
1209 //
1210 // Also, we need this to be a zero-sized, so that the compiler doesn't
1211 // assume anything about the layout.
1212 //
1213 // Use `object_getClass` instead.
1214 _priv: [u8; 0],
1215 _p: ffi::OpaqueData,
1216}
1217
1218/// Use [`AnyObject`] instead.
1219#[deprecated = "renamed to `runtime::AnyObject`. Consider using the correct type from the autogenerated `objc2-*` framework crates instead though"]
1220pub type Object = AnyObject;
1221
1222unsafe impl RefEncode for AnyObject {
1223 const ENCODING_REF: Encoding = Encoding::Object;
1224}
1225
1226// SAFETY: This is technically slightly wrong, not all objects implement the
1227// standard memory management methods. But not having this impl would be too
1228// restrictive, so we'll live with it.
1229//
1230// NOTE: AnyObject actually resolves to the class "Object" internally, but we
1231// don't want to expose that publicly, so we only implement Message here, not
1232// ClassType.
1233unsafe impl Message for AnyObject {}
1234
1235impl AnyObject {
1236 /// Dynamically find the class of this object.
1237 ///
1238 ///
1239 /// # Panics
1240 ///
1241 /// May panic if the object is invalid (which may be the case for objects
1242 /// returned from unavailable `init`/`new` methods).
1243 ///
1244 ///
1245 /// # Example
1246 ///
1247 /// Check that an instance of `NSObject` has the precise class `NSObject`.
1248 ///
1249 /// ```
1250 /// use objc2::ClassType;
1251 /// use objc2::runtime::NSObject;
1252 ///
1253 /// let obj = NSObject::new();
1254 /// assert_eq!(obj.class(), NSObject::class());
1255 /// ```
1256 #[inline]
1257 #[doc(alias = "object_getClass")]
1258 pub fn class(&self) -> &'static AnyClass {
1259 let ptr = unsafe { ffi::object_getClass(self) };
1260 // SAFETY: The pointer is valid, and it is safe as `'static` since
1261 // classes are static (can also be verified with the fact that they
1262 // can be retrieved via `AnyClass::get(self.class().name())`).
1263 let cls = unsafe { ptr.as_ref() };
1264
1265 // The class _should_ not be NULL, because the docs only say that if
1266 // the object is NULL, the class also is; and in practice, certain
1267 // invalid objects can contain a NULL isa pointer.
1268 cls.unwrap_or_else(|| panic!("invalid object {:?} (had NULL class)", self as *const Self))
1269 }
1270
1271 /// Change the class of the object at runtime.
1272 ///
1273 /// Returns the object's previous class.
1274 ///
1275 ///
1276 /// # Safety
1277 ///
1278 /// The new class must:
1279 ///
1280 /// 1. Be a subclass of the object's current class.
1281 ///
1282 /// 2. The subclass must not add any instance variables - importantly, the
1283 /// instance size of old and the new classes must be the same.
1284 ///
1285 /// 3. Any overridden methods on the new class must be fully compatible
1286 /// with the old ones.
1287 ///
1288 /// Note that in the general case, where arbitrary parts of the program
1289 /// may be trying to modify the class of the object concurrently, these
1290 /// requirements are not actually possible to uphold.
1291 ///
1292 /// Since usage of this function is expected to be extremely rare, and
1293 /// even more so trying to do it concurrently, it is recommended that you
1294 /// verify that the returned class is what you would expect, and if not,
1295 /// panic.
1296 #[inline]
1297 #[doc(alias = "object_setClass")]
1298 pub unsafe fn set_class<'s>(this: &Self, cls: &AnyClass) -> &'s AnyClass {
1299 let this: *const Self = this;
1300 let this = this as *mut Self;
1301 let ptr = unsafe { ffi::object_setClass(this, cls) };
1302 // SAFETY: The class is not NULL because the object is not NULL.
1303 let old_cls = unsafe { ptr.as_ref().unwrap_unchecked() };
1304 // TODO: Check the superclass requirement too?
1305 debug_assert_eq!(
1306 old_cls.instance_size(),
1307 cls.instance_size(),
1308 "old and new class sizes were not equal; this is UB!"
1309 );
1310 old_cls
1311 }
1312
1313 /// Offset an object pointer to get a pointer to an ivar.
1314 ///
1315 ///
1316 /// # Safety
1317 ///
1318 /// The offset must be valid for the given type.
1319 #[inline]
1320 pub(crate) unsafe fn ivar_at_offset<T>(ptr: NonNull<Self>, offset: isize) -> NonNull<T> {
1321 // `offset` is given in bytes, so we convert to `u8` and back to `T`
1322 let ptr: NonNull<u8> = ptr.cast();
1323 let ptr: *mut u8 = ptr.as_ptr();
1324 // SAFETY: The offset is valid
1325 let ptr: *mut u8 = unsafe { ptr.offset(offset) };
1326 // SAFETY: The offset operation is guaranteed to not end up computing
1327 // a NULL pointer.
1328 let ptr: NonNull<u8> = unsafe { NonNull::new_unchecked(ptr) };
1329 let ptr: NonNull<T> = ptr.cast();
1330 ptr
1331 }
1332
1333 pub(crate) fn lookup_instance_variable_dynamically(&self, name: &str) -> &'static Ivar {
1334 let name = CString::new(name).unwrap();
1335 let cls = self.class();
1336 cls.instance_variable(&name)
1337 .unwrap_or_else(|| panic!("ivar {name:?} not found on class {cls}"))
1338 }
1339
1340 /// Use [`Ivar::load`] instead.
1341 ///
1342 ///
1343 /// # Safety
1344 ///
1345 /// The object must have an instance variable with the given name, and it
1346 /// must be of type `T`.
1347 ///
1348 /// See [`Ivar::load_ptr`] for details surrounding this.
1349 #[deprecated = "this is difficult to use correctly, use `Ivar::load` instead."]
1350 pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
1351 let ivar = self.lookup_instance_variable_dynamically(name);
1352 // SAFETY: Upheld by caller
1353 unsafe { ivar.load::<T>(self) }
1354 }
1355
1356 /// Use [`Ivar::load_mut`] instead.
1357 ///
1358 ///
1359 /// # Safety
1360 ///
1361 /// The object must have an instance variable with the given name, and it
1362 /// must be of type `T`.
1363 ///
1364 /// See [`Ivar::load_ptr`] for details surrounding this.
1365 #[deprecated = "this is difficult to use correctly, use `Ivar::load_mut` instead."]
1366 pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
1367 let ivar = self.lookup_instance_variable_dynamically(name);
1368 // SAFETY: Upheld by caller
1369 unsafe { ivar.load_mut::<T>(self) }
1370 }
1371
1372 pub(crate) fn is_kind_of_class(&self, cls: &AnyClass) -> Bool {
1373 // SAFETY: The signature is correct.
1374 //
1375 // Note that `isKindOfClass:` is not available on every object, but it
1376 // is still safe to _use_, since the runtime will simply crash if the
1377 // selector isn't implemented. This is of course not _ideal_, but it
1378 // works for all of Apple's Objective-C classes, and it's what Swift
1379 // does.
1380 //
1381 // In theory, someone could have made a root object, and overwritten
1382 // `isKindOfClass:` to do something bogus - but that would conflict
1383 // with normal Objective-C code as well, so we will consider such a
1384 // thing unsound by construction.
1385 unsafe { msg_send![self, isKindOfClass: cls] }
1386 }
1387
1388 /// Attempt to downcast the object to a class of type `T`.
1389 ///
1390 /// This is the reference-variant. Use [`Retained::downcast`] if you want
1391 /// to convert a retained object to another type.
1392 ///
1393 /// [`Retained::downcast`]: crate::rc::Retained::downcast
1394 ///
1395 ///
1396 /// # Mutable classes
1397 ///
1398 /// Some classes have immutable and mutable variants, such as `NSString`
1399 /// and `NSMutableString`.
1400 ///
1401 /// When some Objective-C API signature says it gives you an immutable
1402 /// class, it generally expects you to not mutate that, even though it may
1403 /// technically be mutable "under the hood".
1404 ///
1405 /// So using this method to convert a `NSString` to a `NSMutableString`,
1406 /// while not unsound, is generally frowned upon unless you created the
1407 /// string yourself, or the API explicitly documents the string to be
1408 /// mutable.
1409 ///
1410 /// See Apple's [documentation on mutability][apple-mut] and [on
1411 /// `isKindOfClass:`][iskindof-doc] for more details.
1412 ///
1413 /// [iskindof-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418511-iskindofclass?language=objc
1414 /// [apple-mut]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectMutability/ObjectMutability.html
1415 ///
1416 ///
1417 /// # Generic classes
1418 ///
1419 /// Objective-C generics are called "lightweight generics", and that's
1420 /// because they aren't exposed in the runtime. This makes it impossible
1421 /// to safely downcast to generic collections, so this is disallowed by
1422 /// this method.
1423 ///
1424 /// You can, however, safely downcast to generic collections where all the
1425 /// type-parameters are [`AnyObject`].
1426 ///
1427 ///
1428 /// # Panics
1429 ///
1430 /// This works internally by calling `isKindOfClass:`. That means that the
1431 /// object must have the instance method of that name, and an exception
1432 /// will be thrown (if CoreFoundation is linked) or the process will abort
1433 /// if that is not the case. In the vast majority of cases, you don't need
1434 /// to worry about this, since both root objects [`NSObject`] and
1435 /// `NSProxy` implement this method.
1436 ///
1437 ///
1438 /// # Examples
1439 ///
1440 /// Cast an `NSString` back and forth from `NSObject`.
1441 ///
1442 /// ```
1443 /// use objc2::rc::Retained;
1444 /// use objc2_foundation::{NSObject, NSString};
1445 ///
1446 /// let obj: Retained<NSObject> = NSString::new().into_super();
1447 /// let string = obj.downcast_ref::<NSString>().unwrap();
1448 /// // Or with `downcast`, if we do not need the object afterwards
1449 /// let string = obj.downcast::<NSString>().unwrap();
1450 /// ```
1451 ///
1452 /// Try (and fail) to cast an `NSObject` to an `NSString`.
1453 ///
1454 /// ```
1455 /// use objc2_foundation::{NSObject, NSString};
1456 ///
1457 /// let obj = NSObject::new();
1458 /// assert!(obj.downcast_ref::<NSString>().is_none());
1459 /// ```
1460 ///
1461 /// Try to cast to an array of strings.
1462 ///
1463 /// ```compile_fail,E0277
1464 /// use objc2_foundation::{NSArray, NSObject, NSString};
1465 ///
1466 /// let arr = NSArray::from_retained_slice(&[NSObject::new()]);
1467 /// // This is invalid and doesn't type check.
1468 /// let arr = arr.downcast_ref::<NSArray<NSString>>();
1469 /// ```
1470 ///
1471 /// This fails to compile, since it would require enumerating over the
1472 /// array to ensure that each element is of the desired type, which is a
1473 /// performance pitfall.
1474 ///
1475 /// Downcast when processing each element instead.
1476 ///
1477 /// ```
1478 /// use objc2_foundation::{NSArray, NSObject, NSString};
1479 ///
1480 /// let arr = NSArray::from_retained_slice(&[NSObject::new()]);
1481 ///
1482 /// for elem in arr {
1483 /// if let Some(data) = elem.downcast_ref::<NSString>() {
1484 /// // handle `data`
1485 /// }
1486 /// }
1487 /// ```
1488 #[inline]
1489 pub fn downcast_ref<T: DowncastTarget>(&self) -> Option<&T> {
1490 if self.is_kind_of_class(T::class()).as_bool() {
1491 // SAFETY: Just checked that the object is a class of type `T`.
1492 //
1493 // Generic `T` like `NSArray<NSString>` are ruled out by
1494 // `T: DowncastTarget`.
1495 Some(unsafe { &*(self as *const Self).cast::<T>() })
1496 } else {
1497 None
1498 }
1499 }
1500
1501 // objc_setAssociatedObject
1502 // objc_getAssociatedObject
1503 // objc_removeAssociatedObjects
1504}
1505
1506impl fmt::Debug for AnyObject {
1507 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1508 let ptr: *const Self = self;
1509 write!(f, "<{}: {:p}>", self.class(), ptr)
1510 }
1511}
1512
1513#[cfg(test)]
1514mod tests {
1515 use alloc::ffi::CString;
1516 use alloc::format;
1517 use alloc::string::ToString;
1518 use core::mem::size_of;
1519
1520 use super::*;
1521 use crate::test_utils;
1522 use crate::{class, msg_send, sel, ClassType, ProtocolType};
1523
1524 // TODO: Remove once c"" strings are in MSRV
1525 fn c(s: &str) -> CString {
1526 CString::new(s).unwrap()
1527 }
1528
1529 #[test]
1530 fn test_selector() {
1531 macro_rules! test_sel {
1532 ($s:literal, $($tt:tt)+) => {{
1533 let sel = sel!($($tt)*);
1534 let expected = Sel::register(&c($s));
1535 assert_eq!(sel, expected);
1536 assert_eq!(sel.name().to_str(), Ok($s));
1537 }}
1538 }
1539 test_sel!("abc", abc);
1540 test_sel!("abc:", abc:);
1541 test_sel!("abc:def:", abc:def:);
1542 test_sel!("abc:def:ghi:", abc:def:ghi:);
1543 test_sel!("functionWithControlPoints::::", functionWithControlPoints::::);
1544 test_sel!("initWithControlPoints::::", initWithControlPoints::::);
1545 test_sel!("setFloatValue::", setFloatValue::);
1546 test_sel!("isSupported::", isSupported::);
1547 test_sel!("addEventListener:::", addEventListener:::);
1548 test_sel!("test::arg::", test::arg::);
1549 test_sel!("test::::with::spaces::", test : :: : with : : spaces : :);
1550 test_sel!("a::b:", a::b:);
1551 }
1552
1553 #[test]
1554 fn test_empty_selector() {
1555 let s = c("");
1556 let sel = Sel::register(&s);
1557 assert_eq!(sel.name(), &*s);
1558 let s = c(":");
1559 let sel = Sel::register(&s);
1560 assert_eq!(sel.name(), &*s);
1561 let s = c("::");
1562 let sel = Sel::register(&s);
1563 assert_eq!(sel.name(), &*s);
1564 }
1565
1566 #[test]
1567 fn test_ivar() {
1568 let cls = test_utils::custom_class();
1569 let ivar = cls.instance_variable(&c("_foo")).unwrap();
1570 assert_eq!(ivar.name(), &*c("_foo"));
1571 assert!(<u32>::ENCODING.equivalent_to_str(ivar.type_encoding().to_str().unwrap()));
1572 assert!(ivar.offset() > 0);
1573 assert!(cls.instance_variables().len() > 0);
1574 }
1575
1576 #[test]
1577 fn test_instance_method() {
1578 let cls = test_utils::custom_class();
1579 let sel = Sel::register(&c("foo"));
1580 let method = cls.instance_method(sel).unwrap();
1581 assert_eq!(method.name().name(), &*c("foo"));
1582 assert_eq!(method.arguments_count(), 2);
1583
1584 assert!(<u32>::ENCODING.equivalent_to_str(method.return_type().to_str().unwrap()));
1585 assert!(Sel::ENCODING.equivalent_to_str(method.argument_type(1).unwrap().to_str().unwrap()));
1586
1587 assert!(cls.instance_methods().contains(&method));
1588 }
1589
1590 #[test]
1591 fn test_class_method() {
1592 let cls = test_utils::custom_class();
1593 let method = cls.class_method(sel!(classFoo)).unwrap();
1594 assert_eq!(method.name().name(), &*c("classFoo"));
1595 assert_eq!(method.arguments_count(), 2);
1596
1597 assert!(<u32>::ENCODING.equivalent_to_str(method.return_type().to_str().unwrap()));
1598 assert!(Sel::ENCODING.equivalent_to_str(method.argument_type(1).unwrap().to_str().unwrap()));
1599
1600 assert!(cls.metaclass().instance_methods().contains(&method));
1601 }
1602
1603 #[test]
1604 fn test_class() {
1605 let cls = test_utils::custom_class();
1606 assert_eq!(cls.name(), &*c("CustomObject"));
1607 assert!(cls.instance_size() > 0);
1608 assert!(cls.superclass().is_none());
1609
1610 assert!(cls.responds_to(sel!(foo)));
1611 assert!(cls.responds_to(sel!(setBar:)));
1612 assert!(cls.responds_to(sel!(test::test::)));
1613 assert!(!cls.responds_to(sel!(abc)));
1614 assert!(!cls.responds_to(sel!(addNumber:toNumber:)));
1615
1616 assert_eq!(AnyClass::get(cls.name()), Some(cls));
1617
1618 let metaclass = cls.metaclass();
1619 // The metaclass of a root class is a subclass of the root class
1620 assert_eq!(metaclass.superclass().unwrap(), cls);
1621 assert!(metaclass.responds_to(sel!(addNumber:toNumber:)));
1622 assert!(metaclass.responds_to(sel!(test::test::)));
1623 // TODO: This is unexpected!
1624 assert!(metaclass.responds_to(sel!(foo)));
1625
1626 let subclass = test_utils::custom_subclass();
1627 assert_eq!(subclass.superclass().unwrap(), cls);
1628 }
1629
1630 #[test]
1631 fn test_classes_count() {
1632 assert!(AnyClass::classes_count() > 0);
1633 }
1634
1635 #[test]
1636 fn test_classes() {
1637 let classes = AnyClass::classes();
1638 assert!(classes.len() > 0);
1639 }
1640
1641 #[test]
1642 fn test_protocol() {
1643 let proto = test_utils::custom_protocol();
1644 assert_eq!(proto.name(), &*c("CustomProtocol"));
1645 let class = test_utils::custom_class();
1646 assert!(class.conforms_to(proto));
1647
1648 // The selectors are broken somehow on GNUStep < 2.0
1649 if cfg!(any(not(feature = "gnustep-1-7"), feature = "gnustep-2-0")) {
1650 let desc = MethodDescription {
1651 sel: sel!(setBar:),
1652 types: CStr::from_bytes_with_nul(b"v@:i\0").unwrap(),
1653 };
1654 assert_eq!(&proto.method_descriptions(true), &[desc]);
1655 let desc = MethodDescription {
1656 sel: sel!(getName),
1657 types: CStr::from_bytes_with_nul(b"*@:\0").unwrap(),
1658 };
1659 assert_eq!(&proto.method_descriptions(false), &[desc]);
1660 let desc = MethodDescription {
1661 sel: sel!(addNumber:toNumber:),
1662 types: CStr::from_bytes_with_nul(b"i@:ii\0").unwrap(),
1663 };
1664 assert_eq!(&proto.class_method_descriptions(true), &[desc]);
1665 }
1666 assert_eq!(&proto.class_method_descriptions(false), &[]);
1667
1668 assert!(class.adopted_protocols().contains(&proto));
1669 }
1670
1671 #[test]
1672 fn test_protocol_method() {
1673 let class = test_utils::custom_class();
1674 let result: i32 = unsafe { msg_send![class, addNumber: 1, toNumber: 2] };
1675 assert_eq!(result, 3);
1676 }
1677
1678 #[test]
1679 fn class_self() {
1680 let cls = NSObject::class();
1681 let result: &'static AnyClass = unsafe { msg_send![cls, self] };
1682 assert_eq!(cls, result);
1683 }
1684
1685 #[test]
1686 fn test_subprotocols() {
1687 let sub_proto = test_utils::custom_subprotocol();
1688 let super_proto = test_utils::custom_protocol();
1689 assert!(sub_proto.conforms_to(super_proto));
1690 assert_eq!(sub_proto.adopted_protocols()[0], super_proto);
1691 }
1692
1693 #[test]
1694 fn test_protocols() {
1695 // Ensure that a protocol has been registered on linux
1696 let _ = test_utils::custom_protocol();
1697
1698 assert!(AnyProtocol::protocols().len() > 0);
1699 }
1700
1701 #[test]
1702 fn test_object() {
1703 let obj = test_utils::custom_object();
1704 let cls = test_utils::custom_class();
1705 assert_eq!(obj.class(), cls);
1706
1707 let ivar = cls.instance_variable(&c("_foo")).unwrap();
1708
1709 unsafe { *ivar.load_ptr::<u32>(&obj) = 4 };
1710 let result = unsafe { *ivar.load::<u32>(&obj) };
1711 assert_eq!(result, 4);
1712 }
1713
1714 #[test]
1715 fn test_object_ivar_unknown() {
1716 let cls = test_utils::custom_class();
1717 assert_eq!(cls.instance_variable(&c("unknown")), None);
1718 }
1719
1720 #[test]
1721 fn test_no_ivars() {
1722 let cls = ClassBuilder::new(&c("NoIvarObject"), NSObject::class())
1723 .unwrap()
1724 .register();
1725 assert_eq!(cls.instance_variables().len(), 0);
1726 }
1727
1728 #[test]
1729 #[cfg_attr(
1730 all(debug_assertions, not(feature = "disable-encoding-assertions")),
1731 should_panic = "wrong encoding. Tried to retrieve ivar with encoding I, but the encoding of the given type was C"
1732 )]
1733 fn test_object_ivar_wrong_type() {
1734 let obj = test_utils::custom_object();
1735 let cls = test_utils::custom_class();
1736 let ivar = cls.instance_variable(&c("_foo")).unwrap();
1737 let _ = unsafe { *ivar.load::<u8>(&obj) };
1738 }
1739
1740 #[test]
1741 fn test_encode() {
1742 fn assert_enc<T: Encode>(expected: &str) {
1743 assert_eq!(&T::ENCODING.to_string(), expected);
1744 }
1745 assert_enc::<&AnyObject>("@");
1746 assert_enc::<*mut AnyObject>("@");
1747 assert_enc::<&AnyClass>("#");
1748 assert_enc::<Sel>(":");
1749 assert_enc::<Option<Sel>>(":");
1750 assert_enc::<Imp>("^?");
1751 assert_enc::<Option<Imp>>("^?");
1752 assert_enc::<&AnyProtocol>("@");
1753 }
1754
1755 #[test]
1756 fn test_send_sync() {
1757 fn assert_send_sync<T: Send + Sync + ?Sized>() {}
1758 assert_send_sync::<Bool>();
1759 assert_send_sync::<AnyClass>();
1760 assert_send_sync::<Ivar>();
1761 assert_send_sync::<Method>();
1762 assert_send_sync::<AnyProtocol>();
1763 assert_send_sync::<Sel>();
1764 }
1765
1766 #[test]
1767 fn test_debug_display() {
1768 let sel = sel!(abc:);
1769 assert_eq!(format!("{sel}"), "abc:");
1770 assert_eq!(format!("{sel:?}"), "Sel(\"abc:\")");
1771 let cls = test_utils::custom_class();
1772 assert_eq!(format!("{cls}"), "CustomObject");
1773 assert_eq!(
1774 format!("{cls:?}"),
1775 "AnyClass { name: \"CustomObject\", .. }"
1776 );
1777 let protocol = test_utils::custom_protocol();
1778 assert_eq!(format!("{protocol}"), "CustomProtocol");
1779 assert_eq!(
1780 format!("{protocol:?}"),
1781 "AnyProtocol { name: \"CustomProtocol\", .. }"
1782 );
1783
1784 let object = test_utils::custom_object();
1785 assert_eq!(
1786 format!("{:?}", &*object),
1787 format!("CustomObject(<CustomObject: {:p}>)", &*object)
1788 );
1789 }
1790
1791 #[test]
1792 fn test_multiple_colon() {
1793 let class = test_utils::custom_class();
1794 let res: i32 = unsafe {
1795 MessageReceiver::send_message(class, sel!(test::test::), (1i32, 2i32, 3i32, 4i32))
1796 };
1797 assert_eq!(res, 10);
1798
1799 let obj = test_utils::custom_object();
1800 let res: i32 = unsafe {
1801 MessageReceiver::send_message(&*obj, sel!(test::test::), (1i32, 2i32, 3i32, 4i32))
1802 };
1803 assert_eq!(res, 24);
1804 }
1805
1806 #[test]
1807 fn test_sizes() {
1808 assert_eq!(size_of::<Sel>(), size_of::<*const ()>());
1809 assert_eq!(size_of::<Sel>(), size_of::<Option<Sel>>());
1810
1811 // These must be zero-sized until we get extern types, otherwise the
1812 // optimizer may invalidly assume something about their layout.
1813 assert_eq!(size_of::<AnyClass>(), 0);
1814 assert_eq!(size_of::<AnyObject>(), 0);
1815 assert_eq!(size_of::<AnyProtocol>(), 0);
1816 assert_eq!(size_of::<Ivar>(), 0);
1817 assert_eq!(size_of::<Method>(), 0);
1818 }
1819
1820 fn get_ivar_layout(cls: &AnyClass) -> *const u8 {
1821 unsafe { ffi::class_getIvarLayout(cls) }
1822 }
1823
1824 #[test]
1825 #[cfg_attr(
1826 feature = "gnustep-1-7",
1827 ignore = "ivar layout is still used on GNUStep"
1828 )]
1829 fn test_layout_does_not_matter_any_longer() {
1830 assert!(get_ivar_layout(class!(NSObject)).is_null());
1831 assert!(get_ivar_layout(class!(NSArray)).is_null());
1832 assert!(get_ivar_layout(class!(NSException)).is_null());
1833 assert!(get_ivar_layout(class!(NSNumber)).is_null());
1834 assert!(get_ivar_layout(class!(NSString)).is_null());
1835 }
1836
1837 #[test]
1838 fn test_non_utf8_roundtrip() {
1839 // Some invalid UTF-8 character
1840 let s = CStr::from_bytes_with_nul(b"\x9F\0").unwrap();
1841
1842 let sel = Sel::register(s);
1843 assert_eq!(sel.name(), s);
1844 assert_eq!(sel.to_string(), char::REPLACEMENT_CHARACTER.to_string());
1845
1846 let cls = ClassBuilder::new(s, NSObject::class()).unwrap().register();
1847 assert_eq!(cls.name(), s);
1848 assert_eq!(cls.to_string(), char::REPLACEMENT_CHARACTER.to_string());
1849
1850 let cls_runtime = AnyClass::get(s).unwrap();
1851 assert_eq!(cls, cls_runtime);
1852 }
1853
1854 #[test]
1855 fn class_is_object() {
1856 let cls = NSObject::class();
1857 let retained = cls.retain();
1858 assert_eq!(&*retained, cls);
1859
1860 let obj: &AnyObject = cls.as_ref();
1861 let superclass = obj.class();
1862 assert!(superclass.conforms_to(<dyn NSObjectProtocol>::protocol().unwrap()));
1863
1864 // Classes are NSObject subclasses in the current runtime.
1865 let ns_obj = retained.downcast::<NSObject>().unwrap();
1866 // Test that we can call NSObject methods on classes.
1867 assert_eq!(ns_obj, ns_obj);
1868 let _ = ns_obj.retainCount();
1869 }
1870
1871 #[test]
1872 fn class_has_infinite_retain_count() {
1873 let obj: &AnyObject = NSObject::class().as_ref();
1874 let obj = obj.downcast_ref::<NSObject>().unwrap();
1875
1876 let large_retain = if cfg!(feature = "gnustep-1-7") {
1877 u32::MAX as usize
1878 } else {
1879 usize::MAX
1880 };
1881
1882 assert_eq!(obj.retainCount(), large_retain);
1883 let obj2 = obj.retain();
1884 assert_eq!(obj.retainCount(), large_retain);
1885 drop(obj2);
1886 assert_eq!(obj.retainCount(), large_retain);
1887 }
1888
1889 #[test]
1890 fn protocol_is_object() {
1891 let protocol = <dyn NSObjectProtocol>::protocol().unwrap();
1892 let retained = protocol.retain();
1893 assert_eq!(&*retained, protocol);
1894
1895 // Protocols don't implement isKindOfClass: on GNUStep.
1896 if cfg!(feature = "gnustep-1-7") {
1897 return;
1898 }
1899
1900 // In the old runtime, NSObjectProtocol are not NSObject subclasses.
1901 if cfg!(all(target_os = "macos", target_arch = "x86")) {
1902 let _ = retained.downcast::<NSObject>().unwrap_err();
1903 } else {
1904 // But elsewhere they are.
1905 let obj = retained.downcast::<NSObject>().unwrap();
1906 // Test that we can call NSObject methods on protocols.
1907 assert_eq!(obj, obj);
1908 let _ = obj.retainCount();
1909 }
1910 }
1911}