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 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 /// # Example
1240 ///
1241 /// Check that an instance of `NSObject` has the precise class `NSObject`.
1242 ///
1243 /// ```
1244 /// use objc2::ClassType;
1245 /// use objc2::runtime::NSObject;
1246 ///
1247 /// let obj = NSObject::new();
1248 /// assert_eq!(obj.class(), NSObject::class());
1249 /// ```
1250 #[inline]
1251 #[doc(alias = "object_getClass")]
1252 pub fn class(&self) -> &'static AnyClass {
1253 let ptr = unsafe { ffi::object_getClass(self) };
1254 // SAFETY: The class is not NULL because the object is not NULL, and
1255 // it is safe as `'static` since classes are static, and it could be
1256 // retrieved via `AnyClass::get(self.class().name())` anyhow.
1257 unsafe { ptr.as_ref().unwrap_unchecked() }
1258 }
1259
1260 /// Change the class of the object at runtime.
1261 ///
1262 /// Returns the object's previous class.
1263 ///
1264 ///
1265 /// # Safety
1266 ///
1267 /// The new class must:
1268 ///
1269 /// 1. Be a subclass of the object's current class.
1270 ///
1271 /// 2. The subclass must not add any instance variables - importantly, the
1272 /// instance size of old and the new classes must be the same.
1273 ///
1274 /// 3. Any overridden methods on the new class must be fully compatible
1275 /// with the old ones.
1276 ///
1277 /// Note that in the general case, where arbitrary parts of the program
1278 /// may be trying to modify the class of the object concurrently, these
1279 /// requirements are not actually possible to uphold.
1280 ///
1281 /// Since usage of this function is expected to be extremely rare, and
1282 /// even more so trying to do it concurrently, it is recommended that you
1283 /// verify that the returned class is what you would expect, and if not,
1284 /// panic.
1285 #[inline]
1286 #[doc(alias = "object_setClass")]
1287 pub unsafe fn set_class<'s>(this: &Self, cls: &AnyClass) -> &'s AnyClass {
1288 let this: *const Self = this;
1289 let this = this as *mut Self;
1290 let ptr = unsafe { ffi::object_setClass(this, cls) };
1291 // SAFETY: The class is not NULL because the object is not NULL.
1292 let old_cls = unsafe { ptr.as_ref().unwrap_unchecked() };
1293 // TODO: Check the superclass requirement too?
1294 debug_assert_eq!(
1295 old_cls.instance_size(),
1296 cls.instance_size(),
1297 "old and new class sizes were not equal; this is UB!"
1298 );
1299 old_cls
1300 }
1301
1302 /// Offset an object pointer to get a pointer to an ivar.
1303 ///
1304 ///
1305 /// # Safety
1306 ///
1307 /// The offset must be valid for the given type.
1308 #[inline]
1309 pub(crate) unsafe fn ivar_at_offset<T>(ptr: NonNull<Self>, offset: isize) -> NonNull<T> {
1310 // `offset` is given in bytes, so we convert to `u8` and back to `T`
1311 let ptr: NonNull<u8> = ptr.cast();
1312 let ptr: *mut u8 = ptr.as_ptr();
1313 // SAFETY: The offset is valid
1314 let ptr: *mut u8 = unsafe { ptr.offset(offset) };
1315 // SAFETY: The offset operation is guaranteed to not end up computing
1316 // a NULL pointer.
1317 let ptr: NonNull<u8> = unsafe { NonNull::new_unchecked(ptr) };
1318 let ptr: NonNull<T> = ptr.cast();
1319 ptr
1320 }
1321
1322 pub(crate) fn lookup_instance_variable_dynamically(&self, name: &str) -> &'static Ivar {
1323 let name = CString::new(name).unwrap();
1324 let cls = self.class();
1325 cls.instance_variable(&name)
1326 .unwrap_or_else(|| panic!("ivar {name:?} not found on class {cls}"))
1327 }
1328
1329 /// Use [`Ivar::load`] instead.
1330 ///
1331 ///
1332 /// # Safety
1333 ///
1334 /// The object must have an instance variable with the given name, and it
1335 /// must be of type `T`.
1336 ///
1337 /// See [`Ivar::load_ptr`] for details surrounding this.
1338 #[deprecated = "this is difficult to use correctly, use `Ivar::load` instead."]
1339 pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
1340 let ivar = self.lookup_instance_variable_dynamically(name);
1341 // SAFETY: Upheld by caller
1342 unsafe { ivar.load::<T>(self) }
1343 }
1344
1345 /// Use [`Ivar::load_mut`] instead.
1346 ///
1347 ///
1348 /// # Safety
1349 ///
1350 /// The object must have an instance variable with the given name, and it
1351 /// must be of type `T`.
1352 ///
1353 /// See [`Ivar::load_ptr`] for details surrounding this.
1354 #[deprecated = "this is difficult to use correctly, use `Ivar::load_mut` instead."]
1355 pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
1356 let ivar = self.lookup_instance_variable_dynamically(name);
1357 // SAFETY: Upheld by caller
1358 unsafe { ivar.load_mut::<T>(self) }
1359 }
1360
1361 pub(crate) fn is_kind_of_class(&self, cls: &AnyClass) -> Bool {
1362 // SAFETY: The signature is correct.
1363 //
1364 // Note that `isKindOfClass:` is not available on every object, but it
1365 // is still safe to _use_, since the runtime will simply crash if the
1366 // selector isn't implemented. This is of course not _ideal_, but it
1367 // works for all of Apple's Objective-C classes, and it's what Swift
1368 // does.
1369 //
1370 // In theory, someone could have made a root object, and overwritten
1371 // `isKindOfClass:` to do something bogus - but that would conflict
1372 // with normal Objective-C code as well, so we will consider such a
1373 // thing unsound by construction.
1374 unsafe { msg_send![self, isKindOfClass: cls] }
1375 }
1376
1377 /// Attempt to downcast the object to a class of type `T`.
1378 ///
1379 /// This is the reference-variant. Use [`Retained::downcast`] if you want
1380 /// to convert a retained object to another type.
1381 ///
1382 /// [`Retained::downcast`]: crate::rc::Retained::downcast
1383 ///
1384 ///
1385 /// # Mutable classes
1386 ///
1387 /// Some classes have immutable and mutable variants, such as `NSString`
1388 /// and `NSMutableString`.
1389 ///
1390 /// When some Objective-C API signature says it gives you an immutable
1391 /// class, it generally expects you to not mutate that, even though it may
1392 /// technically be mutable "under the hood".
1393 ///
1394 /// So using this method to convert a `NSString` to a `NSMutableString`,
1395 /// while not unsound, is generally frowned upon unless you created the
1396 /// string yourself, or the API explicitly documents the string to be
1397 /// mutable.
1398 ///
1399 /// See Apple's [documentation on mutability][apple-mut] and [on
1400 /// `isKindOfClass:`][iskindof-doc] for more details.
1401 ///
1402 /// [iskindof-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418511-iskindofclass?language=objc
1403 /// [apple-mut]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectMutability/ObjectMutability.html
1404 ///
1405 ///
1406 /// # Generic classes
1407 ///
1408 /// Objective-C generics are called "lightweight generics", and that's
1409 /// because they aren't exposed in the runtime. This makes it impossible
1410 /// to safely downcast to generic collections, so this is disallowed by
1411 /// this method.
1412 ///
1413 /// You can, however, safely downcast to generic collections where all the
1414 /// type-parameters are [`AnyObject`].
1415 ///
1416 ///
1417 /// # Panics
1418 ///
1419 /// This works internally by calling `isKindOfClass:`. That means that the
1420 /// object must have the instance method of that name, and an exception
1421 /// will be thrown (if CoreFoundation is linked) or the process will abort
1422 /// if that is not the case. In the vast majority of cases, you don't need
1423 /// to worry about this, since both root objects [`NSObject`] and
1424 /// `NSProxy` implement this method.
1425 ///
1426 ///
1427 /// # Examples
1428 ///
1429 /// Cast an `NSString` back and forth from `NSObject`.
1430 ///
1431 /// ```
1432 /// use objc2::rc::Retained;
1433 /// use objc2_foundation::{NSObject, NSString};
1434 ///
1435 /// let obj: Retained<NSObject> = NSString::new().into_super();
1436 /// let string = obj.downcast_ref::<NSString>().unwrap();
1437 /// // Or with `downcast`, if we do not need the object afterwards
1438 /// let string = obj.downcast::<NSString>().unwrap();
1439 /// ```
1440 ///
1441 /// Try (and fail) to cast an `NSObject` to an `NSString`.
1442 ///
1443 /// ```
1444 /// use objc2_foundation::{NSObject, NSString};
1445 ///
1446 /// let obj = NSObject::new();
1447 /// assert!(obj.downcast_ref::<NSString>().is_none());
1448 /// ```
1449 ///
1450 /// Try to cast to an array of strings.
1451 ///
1452 /// ```compile_fail,E0277
1453 /// use objc2_foundation::{NSArray, NSObject, NSString};
1454 ///
1455 /// let arr = NSArray::from_retained_slice(&[NSObject::new()]);
1456 /// // This is invalid and doesn't type check.
1457 /// let arr = arr.downcast_ref::<NSArray<NSString>>();
1458 /// ```
1459 ///
1460 /// This fails to compile, since it would require enumerating over the
1461 /// array to ensure that each element is of the desired type, which is a
1462 /// performance pitfall.
1463 ///
1464 /// Downcast when processing each element instead.
1465 ///
1466 /// ```
1467 /// use objc2_foundation::{NSArray, NSObject, NSString};
1468 ///
1469 /// let arr = NSArray::from_retained_slice(&[NSObject::new()]);
1470 ///
1471 /// for elem in arr {
1472 /// if let Some(data) = elem.downcast_ref::<NSString>() {
1473 /// // handle `data`
1474 /// }
1475 /// }
1476 /// ```
1477 #[inline]
1478 pub fn downcast_ref<T: DowncastTarget>(&self) -> Option<&T> {
1479 if self.is_kind_of_class(T::class()).as_bool() {
1480 // SAFETY: Just checked that the object is a class of type `T`.
1481 //
1482 // Generic `T` like `NSArray<NSString>` are ruled out by
1483 // `T: DowncastTarget`.
1484 Some(unsafe { &*(self as *const Self).cast::<T>() })
1485 } else {
1486 None
1487 }
1488 }
1489
1490 // objc_setAssociatedObject
1491 // objc_getAssociatedObject
1492 // objc_removeAssociatedObjects
1493}
1494
1495impl fmt::Debug for AnyObject {
1496 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1497 let ptr: *const Self = self;
1498 write!(f, "<{}: {:p}>", self.class(), ptr)
1499 }
1500}
1501
1502#[cfg(test)]
1503mod tests {
1504 use alloc::ffi::CString;
1505 use alloc::format;
1506 use alloc::string::ToString;
1507 use core::mem::size_of;
1508
1509 use super::*;
1510 use crate::test_utils;
1511 use crate::{class, msg_send, sel, ClassType, ProtocolType};
1512
1513 // TODO: Remove once c"" strings are in MSRV
1514 fn c(s: &str) -> CString {
1515 CString::new(s).unwrap()
1516 }
1517
1518 #[test]
1519 fn test_selector() {
1520 macro_rules! test_sel {
1521 ($s:literal, $($tt:tt)+) => {{
1522 let sel = sel!($($tt)*);
1523 let expected = Sel::register(&c($s));
1524 assert_eq!(sel, expected);
1525 assert_eq!(sel.name().to_str(), Ok($s));
1526 }}
1527 }
1528 test_sel!("abc", abc);
1529 test_sel!("abc:", abc:);
1530 test_sel!("abc:def:", abc:def:);
1531 test_sel!("abc:def:ghi:", abc:def:ghi:);
1532 test_sel!("functionWithControlPoints::::", functionWithControlPoints::::);
1533 test_sel!("initWithControlPoints::::", initWithControlPoints::::);
1534 test_sel!("setFloatValue::", setFloatValue::);
1535 test_sel!("isSupported::", isSupported::);
1536 test_sel!("addEventListener:::", addEventListener:::);
1537 test_sel!("test::arg::", test::arg::);
1538 test_sel!("test::::with::spaces::", test : :: : with : : spaces : :);
1539 test_sel!("a::b:", a::b:);
1540 }
1541
1542 #[test]
1543 fn test_empty_selector() {
1544 let s = c("");
1545 let sel = Sel::register(&s);
1546 assert_eq!(sel.name(), &*s);
1547 let s = c(":");
1548 let sel = Sel::register(&s);
1549 assert_eq!(sel.name(), &*s);
1550 let s = c("::");
1551 let sel = Sel::register(&s);
1552 assert_eq!(sel.name(), &*s);
1553 }
1554
1555 #[test]
1556 fn test_ivar() {
1557 let cls = test_utils::custom_class();
1558 let ivar = cls.instance_variable(&c("_foo")).unwrap();
1559 assert_eq!(ivar.name(), &*c("_foo"));
1560 assert!(<u32>::ENCODING.equivalent_to_str(ivar.type_encoding().to_str().unwrap()));
1561 assert!(ivar.offset() > 0);
1562 assert!(cls.instance_variables().len() > 0);
1563 }
1564
1565 #[test]
1566 fn test_instance_method() {
1567 let cls = test_utils::custom_class();
1568 let sel = Sel::register(&c("foo"));
1569 let method = cls.instance_method(sel).unwrap();
1570 assert_eq!(method.name().name(), &*c("foo"));
1571 assert_eq!(method.arguments_count(), 2);
1572
1573 assert!(<u32>::ENCODING.equivalent_to_str(method.return_type().to_str().unwrap()));
1574 assert!(Sel::ENCODING.equivalent_to_str(method.argument_type(1).unwrap().to_str().unwrap()));
1575
1576 assert!(cls.instance_methods().iter().any(|m| *m == method));
1577 }
1578
1579 #[test]
1580 fn test_class_method() {
1581 let cls = test_utils::custom_class();
1582 let method = cls.class_method(sel!(classFoo)).unwrap();
1583 assert_eq!(method.name().name(), &*c("classFoo"));
1584 assert_eq!(method.arguments_count(), 2);
1585
1586 assert!(<u32>::ENCODING.equivalent_to_str(method.return_type().to_str().unwrap()));
1587 assert!(Sel::ENCODING.equivalent_to_str(method.argument_type(1).unwrap().to_str().unwrap()));
1588
1589 assert!(cls
1590 .metaclass()
1591 .instance_methods()
1592 .iter()
1593 .any(|m| *m == method));
1594 }
1595
1596 #[test]
1597 fn test_class() {
1598 let cls = test_utils::custom_class();
1599 assert_eq!(cls.name(), &*c("CustomObject"));
1600 assert!(cls.instance_size() > 0);
1601 assert!(cls.superclass().is_none());
1602
1603 assert!(cls.responds_to(sel!(foo)));
1604 assert!(cls.responds_to(sel!(setBar:)));
1605 assert!(cls.responds_to(sel!(test::test::)));
1606 assert!(!cls.responds_to(sel!(abc)));
1607 assert!(!cls.responds_to(sel!(addNumber:toNumber:)));
1608
1609 assert_eq!(AnyClass::get(cls.name()), Some(cls));
1610
1611 let metaclass = cls.metaclass();
1612 // The metaclass of a root class is a subclass of the root class
1613 assert_eq!(metaclass.superclass().unwrap(), cls);
1614 assert!(metaclass.responds_to(sel!(addNumber:toNumber:)));
1615 assert!(metaclass.responds_to(sel!(test::test::)));
1616 // TODO: This is unexpected!
1617 assert!(metaclass.responds_to(sel!(foo)));
1618
1619 let subclass = test_utils::custom_subclass();
1620 assert_eq!(subclass.superclass().unwrap(), cls);
1621 }
1622
1623 #[test]
1624 fn test_classes_count() {
1625 assert!(AnyClass::classes_count() > 0);
1626 }
1627
1628 #[test]
1629 fn test_classes() {
1630 let classes = AnyClass::classes();
1631 assert!(classes.len() > 0);
1632 }
1633
1634 #[test]
1635 fn test_protocol() {
1636 let proto = test_utils::custom_protocol();
1637 assert_eq!(proto.name(), &*c("CustomProtocol"));
1638 let class = test_utils::custom_class();
1639 assert!(class.conforms_to(proto));
1640
1641 // The selectors are broken somehow on GNUStep < 2.0
1642 if cfg!(any(not(feature = "gnustep-1-7"), feature = "gnustep-2-0")) {
1643 let desc = MethodDescription {
1644 sel: sel!(setBar:),
1645 types: CStr::from_bytes_with_nul(b"v@:i\0").unwrap(),
1646 };
1647 assert_eq!(&proto.method_descriptions(true), &[desc]);
1648 let desc = MethodDescription {
1649 sel: sel!(getName),
1650 types: CStr::from_bytes_with_nul(b"*@:\0").unwrap(),
1651 };
1652 assert_eq!(&proto.method_descriptions(false), &[desc]);
1653 let desc = MethodDescription {
1654 sel: sel!(addNumber:toNumber:),
1655 types: CStr::from_bytes_with_nul(b"i@:ii\0").unwrap(),
1656 };
1657 assert_eq!(&proto.class_method_descriptions(true), &[desc]);
1658 }
1659 assert_eq!(&proto.class_method_descriptions(false), &[]);
1660
1661 assert!(class.adopted_protocols().iter().any(|p| *p == proto));
1662 }
1663
1664 #[test]
1665 fn test_protocol_method() {
1666 let class = test_utils::custom_class();
1667 let result: i32 = unsafe { msg_send![class, addNumber: 1, toNumber: 2] };
1668 assert_eq!(result, 3);
1669 }
1670
1671 #[test]
1672 fn class_self() {
1673 let cls = NSObject::class();
1674 let result: &'static AnyClass = unsafe { msg_send![cls, self] };
1675 assert_eq!(cls, result);
1676 }
1677
1678 #[test]
1679 fn test_subprotocols() {
1680 let sub_proto = test_utils::custom_subprotocol();
1681 let super_proto = test_utils::custom_protocol();
1682 assert!(sub_proto.conforms_to(super_proto));
1683 assert_eq!(sub_proto.adopted_protocols()[0], super_proto);
1684 }
1685
1686 #[test]
1687 fn test_protocols() {
1688 // Ensure that a protocol has been registered on linux
1689 let _ = test_utils::custom_protocol();
1690
1691 assert!(AnyProtocol::protocols().len() > 0);
1692 }
1693
1694 #[test]
1695 fn test_object() {
1696 let obj = test_utils::custom_object();
1697 let cls = test_utils::custom_class();
1698 assert_eq!(obj.class(), cls);
1699
1700 let ivar = cls.instance_variable(&c("_foo")).unwrap();
1701
1702 unsafe { *ivar.load_ptr::<u32>(&obj) = 4 };
1703 let result = unsafe { *ivar.load::<u32>(&obj) };
1704 assert_eq!(result, 4);
1705 }
1706
1707 #[test]
1708 fn test_object_ivar_unknown() {
1709 let cls = test_utils::custom_class();
1710 assert_eq!(cls.instance_variable(&c("unknown")), None);
1711 }
1712
1713 #[test]
1714 fn test_no_ivars() {
1715 let cls = ClassBuilder::new(&c("NoIvarObject"), NSObject::class())
1716 .unwrap()
1717 .register();
1718 assert_eq!(cls.instance_variables().len(), 0);
1719 }
1720
1721 #[test]
1722 #[cfg_attr(
1723 all(debug_assertions, not(feature = "disable-encoding-assertions")),
1724 should_panic = "wrong encoding. Tried to retrieve ivar with encoding I, but the encoding of the given type was C"
1725 )]
1726 fn test_object_ivar_wrong_type() {
1727 let obj = test_utils::custom_object();
1728 let cls = test_utils::custom_class();
1729 let ivar = cls.instance_variable(&c("_foo")).unwrap();
1730 let _ = unsafe { *ivar.load::<u8>(&obj) };
1731 }
1732
1733 #[test]
1734 fn test_encode() {
1735 fn assert_enc<T: Encode>(expected: &str) {
1736 assert_eq!(&T::ENCODING.to_string(), expected);
1737 }
1738 assert_enc::<&AnyObject>("@");
1739 assert_enc::<*mut AnyObject>("@");
1740 assert_enc::<&AnyClass>("#");
1741 assert_enc::<Sel>(":");
1742 assert_enc::<Option<Sel>>(":");
1743 assert_enc::<Imp>("^?");
1744 assert_enc::<Option<Imp>>("^?");
1745 assert_enc::<&AnyProtocol>("@");
1746 }
1747
1748 #[test]
1749 fn test_send_sync() {
1750 fn assert_send_sync<T: Send + Sync + ?Sized>() {}
1751 assert_send_sync::<Bool>();
1752 assert_send_sync::<AnyClass>();
1753 assert_send_sync::<Ivar>();
1754 assert_send_sync::<Method>();
1755 assert_send_sync::<AnyProtocol>();
1756 assert_send_sync::<Sel>();
1757 }
1758
1759 #[test]
1760 fn test_debug_display() {
1761 let sel = sel!(abc:);
1762 assert_eq!(format!("{sel}"), "abc:");
1763 assert_eq!(format!("{sel:?}"), "Sel(\"abc:\")");
1764 let cls = test_utils::custom_class();
1765 assert_eq!(format!("{cls}"), "CustomObject");
1766 assert_eq!(
1767 format!("{cls:?}"),
1768 "AnyClass { name: \"CustomObject\", .. }"
1769 );
1770 let protocol = test_utils::custom_protocol();
1771 assert_eq!(format!("{protocol}"), "CustomProtocol");
1772 assert_eq!(
1773 format!("{protocol:?}"),
1774 "AnyProtocol { name: \"CustomProtocol\", .. }"
1775 );
1776
1777 let object = test_utils::custom_object();
1778 assert_eq!(
1779 format!("{:?}", &*object),
1780 format!("CustomObject(<CustomObject: {:p}>)", &*object)
1781 );
1782 }
1783
1784 #[test]
1785 fn test_multiple_colon() {
1786 let class = test_utils::custom_class();
1787 let res: i32 = unsafe {
1788 MessageReceiver::send_message(class, sel!(test::test::), (1i32, 2i32, 3i32, 4i32))
1789 };
1790 assert_eq!(res, 10);
1791
1792 let obj = test_utils::custom_object();
1793 let res: i32 = unsafe {
1794 MessageReceiver::send_message(&*obj, sel!(test::test::), (1i32, 2i32, 3i32, 4i32))
1795 };
1796 assert_eq!(res, 24);
1797 }
1798
1799 #[test]
1800 fn test_sizes() {
1801 assert_eq!(size_of::<Sel>(), size_of::<*const ()>());
1802 assert_eq!(size_of::<Sel>(), size_of::<Option<Sel>>());
1803
1804 // These must be zero-sized until we get extern types, otherwise the
1805 // optimizer may invalidly assume something about their layout.
1806 assert_eq!(size_of::<AnyClass>(), 0);
1807 assert_eq!(size_of::<AnyObject>(), 0);
1808 assert_eq!(size_of::<AnyProtocol>(), 0);
1809 assert_eq!(size_of::<Ivar>(), 0);
1810 assert_eq!(size_of::<Method>(), 0);
1811 }
1812
1813 fn get_ivar_layout(cls: &AnyClass) -> *const u8 {
1814 unsafe { ffi::class_getIvarLayout(cls) }
1815 }
1816
1817 #[test]
1818 #[cfg_attr(
1819 feature = "gnustep-1-7",
1820 ignore = "ivar layout is still used on GNUStep"
1821 )]
1822 fn test_layout_does_not_matter_any_longer() {
1823 assert!(get_ivar_layout(class!(NSObject)).is_null());
1824 assert!(get_ivar_layout(class!(NSArray)).is_null());
1825 assert!(get_ivar_layout(class!(NSException)).is_null());
1826 assert!(get_ivar_layout(class!(NSNumber)).is_null());
1827 assert!(get_ivar_layout(class!(NSString)).is_null());
1828 }
1829
1830 #[test]
1831 fn test_non_utf8_roundtrip() {
1832 // Some invalid UTF-8 character
1833 let s = CStr::from_bytes_with_nul(b"\x9F\0").unwrap();
1834
1835 let sel = Sel::register(s);
1836 assert_eq!(sel.name(), s);
1837 assert_eq!(sel.to_string(), char::REPLACEMENT_CHARACTER.to_string());
1838
1839 let cls = ClassBuilder::new(s, NSObject::class()).unwrap().register();
1840 assert_eq!(cls.name(), s);
1841 assert_eq!(cls.to_string(), char::REPLACEMENT_CHARACTER.to_string());
1842
1843 let cls_runtime = AnyClass::get(s).unwrap();
1844 assert_eq!(cls, cls_runtime);
1845 }
1846
1847 #[test]
1848 fn class_is_object() {
1849 let cls = NSObject::class();
1850 let retained = cls.retain();
1851 assert_eq!(&*retained, cls);
1852
1853 let obj: &AnyObject = cls.as_ref();
1854 let superclass = obj.class();
1855 assert!(superclass.conforms_to(<dyn NSObjectProtocol>::protocol().unwrap()));
1856
1857 // Classes are NSObject subclasses in the current runtime.
1858 let ns_obj = retained.downcast::<NSObject>().unwrap();
1859 // Test that we can call NSObject methods on classes.
1860 assert_eq!(ns_obj, ns_obj);
1861 let _ = ns_obj.retainCount();
1862 }
1863
1864 #[test]
1865 fn class_has_infinite_retain_count() {
1866 let obj: &AnyObject = NSObject::class().as_ref();
1867 let obj = obj.downcast_ref::<NSObject>().unwrap();
1868
1869 let large_retain = if cfg!(feature = "gnustep-1-7") {
1870 u32::MAX as usize
1871 } else {
1872 usize::MAX
1873 };
1874
1875 assert_eq!(obj.retainCount(), large_retain);
1876 let obj2 = obj.retain();
1877 assert_eq!(obj.retainCount(), large_retain);
1878 drop(obj2);
1879 assert_eq!(obj.retainCount(), large_retain);
1880 }
1881
1882 #[test]
1883 fn protocol_is_object() {
1884 let protocol = <dyn NSObjectProtocol>::protocol().unwrap();
1885 let retained = protocol.retain();
1886 assert_eq!(&*retained, protocol);
1887
1888 // Protocols don't implement isKindOfClass: on GNUStep.
1889 if cfg!(feature = "gnustep-1-7") {
1890 return;
1891 }
1892
1893 // In the old runtime, NSObjectProtocol are not NSObject subclasses.
1894 if cfg!(all(target_os = "macos", target_arch = "x86")) {
1895 let _ = retained.downcast::<NSObject>().unwrap_err();
1896 } else {
1897 // But elsewhere they are.
1898 let obj = retained.downcast::<NSObject>().unwrap();
1899 // Test that we can call NSObject methods on protocols.
1900 assert_eq!(obj, obj);
1901 let _ = obj.retainCount();
1902 }
1903 }
1904}