mod __attribute_helpers; mod __fallback; mod __method_msg_send; mod __msg_send_parse; mod __rewrite_self_param; mod available; mod cf_objc2_type; mod define_class; mod extern_class; mod extern_conformance; mod extern_methods; mod extern_protocol; /// Gets a reference to an [`AnyClass`] from the given name. /// /// If you have an object that implements [`ClassType`], consider using the /// [`ClassType::class`] method instead. /// /// [`AnyClass`]: crate::runtime::AnyClass /// [`ClassType`]: crate::ClassType /// [`ClassType::class`]: crate::ClassType::class /// /// /// # Panics /// /// Panics if no class with the given name can be found. /// /// To dynamically check for a class that may not exist, use [`AnyClass::get`]. /// /// [`AnyClass::get`]: crate::runtime::AnyClass::get /// /// /// # Features /// /// If the experimental `"unstable-static-class"` feature is enabled, this /// will emit special statics that will be replaced by dyld when the program /// starts up. /// /// Errors that were previously runtime panics may now turn into linker errors /// if you try to use a class which is not available. Additionally, you may /// have to call `msg_send![cls, class]` on the result if you want to use it /// in a dynamic context (e.g. when dynamically creating classes). /// /// See the [corresponding section][sel#features] in the [`sel!`] macro for /// more details on the limitations of this. The /// `"unstable-static-class-inlined"` corresponds to the /// `"unstable-static-sel-inlined"` feature here. /// /// [sel#features]: crate::sel#features /// [`sel!`]: crate::sel /// /// /// # Examples /// /// Get and compare the class with one returned from [`ClassType::class`]. /// /// ``` /// use objc2::runtime::NSObject; /// use objc2::{class, ClassType}; /// /// let cls1 = class!(NSObject); /// let cls2 = NSObject::class(); /// assert_eq!(cls1, cls2); /// ``` /// /// Try to get a non-existing class (this will panic, or fail to link). /// #[cfg_attr(not(feature = "unstable-static-class"), doc = "```should_panic")] #[cfg_attr(feature = "unstable-static-class", doc = "```ignore")] /// use objc2::class; /// /// let _ = class!(NonExistentClass); /// ``` #[macro_export] macro_rules! class { ($name:ident) => {{ $crate::__class_inner!( $crate::__macro_helpers::stringify!($name), $crate::__hash_idents!($name) ) }}; } #[doc(hidden)] #[macro_export] #[cfg(not(feature = "unstable-static-class"))] macro_rules! __class_inner { ($name:expr, $_hash:expr) => {{ static CACHED_CLASS: $crate::__macro_helpers::CachedClass = $crate::__macro_helpers::CachedClass::new(); #[allow(unused_unsafe)] unsafe { CACHED_CLASS.get($crate::__macro_helpers::concat!($name, '\0')) } }}; } /// Register a selector with the Objective-C runtime. /// /// Returns the [`Sel`] corresponding to the specified selector. /// /// [`Sel`]: crate::runtime::Sel /// /// /// # Panics /// /// Panics if the runtime failed allocating space for the selector. /// /// /// # Specification /// /// This has similar syntax and functionality as the `@selector` directive in /// Objective-C. /// /// This calls [`Sel::register`] internally. The result is cached for /// efficiency. The cache for certain common selectors (`alloc`, `init` and /// `new`) is deduplicated to reduce code-size. /// /// Non-ascii identifiers are ill-tested, if supported at all. /// /// [`Sel::register`]: crate::runtime::Sel::register /// /// /// # Features /// /// If the experimental `"unstable-static-sel"` feature is enabled, this will /// emit special statics that will be replaced by the dynamic linker (dyld) /// when the program starts up - in exactly the same manner as normal /// Objective-C code does. /// This should be significantly faster (and allow better native debugging), /// however due to the Rust compilation model, and since we don't have /// low-level control over it, it is currently unlikely that this will work /// correctly in all cases. /// See the source code and [rust-lang/rust#53929] for more info. /// /// Concretely, this may fail at: /// - link-time (likely) /// - dynamic link-time/just before the program is run (fairly likely) /// - runtime, causing UB (unlikely) /// /// The `"unstable-static-sel-inlined"` feature is the even more extreme /// version - it yields the best performance and is closest to real /// Objective-C code, but probably won't work unless your code and its /// inlining is written in a very certain way. /// /// Enabling LTO greatly increases the chance that these features work. /// /// [rust-lang/rust#53929]: https://github.com/rust-lang/rust/issues/53929 /// /// /// # Examples /// /// Get a few different selectors: /// /// ```rust /// use objc2::sel; /// let sel = sel!(alloc); /// let sel = sel!(description); /// let sel = sel!(_privateMethod); /// let sel = sel!(storyboardWithName:bundle:); /// let sel = sel!( /// otherEventWithType: /// location: /// modifierFlags: /// timestamp: /// windowNumber: /// context: /// subtype: /// data1: /// data2: /// ); /// ``` /// /// Whitespace is ignored: /// /// ``` /// # use objc2::sel; /// let sel1 = sel!(setObject:forKey:); /// let sel2 = sel!( setObject : /// /// forKey : ); /// assert_eq!(sel1, sel2); /// ``` /// /// Invalid selector: /// /// ```compile_fail /// # use objc2::sel; /// let sel = sel!(aSelector:withoutTrailingColon); /// ``` /// /// A selector with internal colons: /// /// ``` /// # use objc2::sel; /// let sel = sel!(sel::with:::multiple:internal::::colons:::); /// /// // Yes, that is possible! The following Objective-C would work: /// // /// // @interface MyThing: NSObject /// // + (void)test:(int)a :(int)b arg:(int)c :(int)d; /// // @end /// ``` /// /// Unsupported usage that you may run into when using macros - fails to /// compile when the `"unstable-static-sel"` feature is enabled. /// /// Instead, define a wrapper function that retrieves the selector. /// #[cfg_attr(not(feature = "unstable-static-sel"), doc = "```no_run")] #[cfg_attr(feature = "unstable-static-sel", doc = "```compile_fail")] /// use objc2::sel; /// macro_rules! x { /// ($x:ident) => { /// // One of these is fine /// sel!($x); /// // But using the identifier again in the same way is not! /// sel!($x); /// }; /// } /// // Identifier `abc` /// x!(abc); /// ``` #[macro_export] macro_rules! sel { (new) => ({ $crate::__macro_helpers::new_sel() }); (init) => ({ $crate::__macro_helpers::init_sel() }); (alloc) => ({ $crate::__macro_helpers::alloc_sel() }); (dealloc) => ({ $crate::__macro_helpers::dealloc_sel() }); ($sel:ident) => ({ $crate::__sel_inner!( $crate::__sel_data!($sel), $crate::__hash_idents!($sel) ) }); ($($sel:ident :)*) => ({ $crate::__sel_inner!( $crate::__sel_data!($($sel :)*), $crate::__hash_idents!($($sel :)*) ) }); ($($sel:tt)*) => { $crate::__sel_inner!( $crate::__sel_helper! { () $($sel)* }, $crate::__hash_idents!($($sel)*) ) }; } /// Handle selectors with internal colons. /// /// Required since `::` is a different token than `:`. #[doc(hidden)] #[macro_export] macro_rules! __sel_helper { // Base-case { ($($parsed_sel:tt)*) } => ({ $crate::__sel_data!($($parsed_sel)*) }); // Single identifier { () $ident:ident } => { $crate::__sel_helper! { ($ident) } }; // Parse identitifer + colon token { ($($parsed_sel:tt)*) $($ident:ident)? : $($rest:tt)* } => { $crate::__sel_helper! { ($($parsed_sel)* $($ident)? :) $($rest)* } }; // Parse identitifer + path separator token { ($($parsed_sel:tt)*) $($ident:ident)? :: $($rest:tt)* } => { $crate::__sel_helper! { // Notice space between these ($($parsed_sel)* $($ident)? : :) $($rest)* } }; } #[doc(hidden)] #[macro_export] macro_rules! __sel_data { ($first:ident $(: $($($rest:ident)? :)*)?) => { $crate::__macro_helpers::concat!( $crate::__macro_helpers::stringify!($first), $(':', $($($crate::__macro_helpers::stringify!($rest),)? ':',)*)? '\0', ) }; } #[doc(hidden)] #[macro_export] #[cfg(not(feature = "unstable-static-sel"))] macro_rules! __sel_inner { ($data:expr, $_hash:expr) => {{ static CACHED_SEL: $crate::__macro_helpers::CachedSel = $crate::__macro_helpers::CachedSel::new(); #[allow(unused_unsafe)] unsafe { CACHED_SEL.get($data) } }}; } #[doc(hidden)] #[macro_export] macro_rules! __statics_string_to_known_length_bytes { ($inp:ident) => {{ // Convert the `&[u8]` slice to an array with known length, so // that we can place that directly in a static. let mut res: [$crate::__macro_helpers::u8; $inp.len()] = [0; $inp.len()]; let mut i = 0; while i < $inp.len() { res[i] = $inp[i]; i += 1; } res }}; } #[doc(hidden)] #[macro_export] #[cfg(target_vendor = "apple")] macro_rules! __statics_image_info { ($hash:expr) => { /// We always emit the image info tag, since we need it to: /// - End up in the same codegen unit as the other statics below. /// - End up in the final binary so it can be read by dyld. /// /// If it's not present in the codegen unit, then `ld64` won't set /// `hasObjC` for that specific object file, and in turn it might /// disable processing of the special Objective-C sections (currently /// a category merging pass, in the future who knows what). /// /// Unfortunately however, this leads to duplicated tags - the linker /// reports `__DATA/__objc_imageinfo has unexpectedly large size XXX`, /// but things still seems to work. #[cfg_attr( not(all(target_os = "macos", target_arch = "x86")), link_section = "__DATA,__objc_imageinfo,regular,no_dead_strip" )] #[cfg_attr( all(target_os = "macos", target_arch = "x86"), link_section = "__OBJC,__image_info,regular" )] #[export_name = $crate::__macro_helpers::concat!("\x01L_OBJC_IMAGE_INFO_", $hash)] #[used] // Make sure this reaches the linker static _IMAGE_INFO: $crate::__macro_helpers::ImageInfo = $crate::__macro_helpers::ImageInfo::system(); }; } #[doc(hidden)] #[macro_export] #[cfg(target_vendor = "apple")] macro_rules! __statics_module_info { ($hash:expr) => { #[link_section = "__TEXT,__cstring,cstring_literals"] #[export_name = $crate::__macro_helpers::concat!("\x01L_OBJC_CLASS_NAME_", $hash, "_MODULE_INFO")] static MODULE_INFO_NAME: [$crate::__macro_helpers::u8; 1] = [0]; /// Emit module info. /// /// This is similar to image info, and must be present in the final /// binary on macOS 32-bit. #[link_section = "__OBJC,__module_info,regular,no_dead_strip"] #[export_name = $crate::__macro_helpers::concat!("\x01L_OBJC_MODULES_", $hash)] #[used] // Make sure this reaches the linker static _MODULE_INFO: $crate::__macro_helpers::ModuleInfo = $crate::__macro_helpers::ModuleInfo::new(MODULE_INFO_NAME.as_ptr()); }; } #[doc(hidden)] #[macro_export] #[cfg(target_vendor = "apple")] macro_rules! __statics_sel { { ($data:expr) ($hash:expr) } => { const X: &[$crate::__macro_helpers::u8] = $data.as_bytes(); /// Clang marks this with LLVM's `unnamed_addr`. /// See rust-lang/rust#18297 /// Should only be an optimization (?) #[cfg_attr( not(all(target_os = "macos", target_arch = "x86")), link_section = "__TEXT,__objc_methname,cstring_literals", )] #[cfg_attr( all(target_os = "macos", target_arch = "x86"), link_section = "__TEXT,__cstring,cstring_literals", )] #[export_name = $crate::__macro_helpers::concat!( "\x01L_OBJC_METH_VAR_NAME_", $hash, )] static NAME_DATA: [$crate::__macro_helpers::u8; X.len()] = $crate::__statics_string_to_known_length_bytes!(X); /// Place the constant value in the correct section. /// /// We use `UnsafeCell` because this somewhat resembles internal /// mutation - this pointer will be changed by dyld at startup, so we /// _must_ prevent Rust/LLVM from trying to "peek inside" it and just /// use a pointer to `NAME_DATA` directly. /// /// Clang does this by marking `REF` with LLVM's /// `externally_initialized`. /// /// /// # Safety /// /// I'm quite uncertain of how safe this is, since the Rust abstract /// machine has no concept of a static that is initialized outside of /// it - perhaps it would be better to use `read_volatile` instead of /// relying on `UnsafeCell`? Or perhaps `MaybeUninit` would help? /// /// See the [`ctor`](https://docs.rs/ctor) crate for more info on /// "life before main". #[cfg_attr( not(all(target_os = "macos", target_arch = "x86")), // Clang uses `no_dead_strip` in the link section for some unknown reason, // but it makes LTO fail to trim the unused symbols. // https://github.com/madsmtm/objc2/issues/667 // https://github.com/llvm/llvm-project/issues/114111 link_section = "__DATA,__objc_selrefs,literal_pointers", )] #[cfg_attr( all(target_os = "macos", target_arch = "x86"), link_section = "__OBJC,__message_refs,literal_pointers", )] #[export_name = $crate::__macro_helpers::concat!("\x01L_OBJC_SELECTOR_REFERENCES_", $hash)] static REF: $crate::__macro_helpers::SyncUnsafeCell<$crate::runtime::Sel> = unsafe { $crate::__macro_helpers::SyncUnsafeCell::new($crate::runtime::Sel::__internal_from_ptr(NAME_DATA.as_ptr())) }; $crate::__statics_image_info!($hash); }; } #[doc(hidden)] #[macro_export] #[cfg(not(target_vendor = "apple"))] macro_rules! __statics_sel { ($($args:tt)*) => { // TODO $crate::__macro_helpers::compile_error!( "The `\"unstable-static-sel\"` feature is not yet supported on GNUStep!" ) }; } #[doc(hidden)] #[macro_export] #[cfg(all( target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")) ))] macro_rules! __statics_class { { ($name:expr) ($hash:expr) } => { extern "C" { /// Link to the Objective-C class static. /// /// This uses the special symbol that static and dynamic linkers /// knows about. /// /// Failure modes: /// - Unknown class: Static linker error. /// - OS version < Class introduced version: Dynamic linker error /// on program startup. /// - Deployment target > Class introduced version: No error, /// though _should_ be a static linker error. /// /// Ideally, we'd have some way of allowing this to be weakly /// linked, and return `Option<&AnyClass>` in that case, but Rust /// doesn't have the capability to do so yet! /// /// /// #[link_name = $crate::__macro_helpers::concat!("OBJC_CLASS_$_", $name)] static CLASS: $crate::runtime::AnyClass; } /// SAFETY: Same as `REF` above in `__statics_sel!`. #[link_section = "__DATA,__objc_classrefs,regular"] #[export_name = $crate::__macro_helpers::concat!( "\x01L_OBJC_CLASSLIST_REFERENCES_$_", $hash, )] static REF: $crate::__macro_helpers::SyncUnsafeCell<&$crate::runtime::AnyClass> = unsafe { $crate::__macro_helpers::SyncUnsafeCell::new(&CLASS) }; $crate::__statics_image_info!($hash); }; } #[doc(hidden)] #[macro_export] #[cfg(all(target_vendor = "apple", all(target_os = "macos", target_arch = "x86")))] macro_rules! __statics_class { { ($name:expr) ($hash:expr) } => { const X: &[$crate::__macro_helpers::u8] = $name.as_bytes(); /// Similar to NAME_DATA above in `__statics_sel!`. #[link_section = "__TEXT,__cstring,cstring_literals"] #[export_name = $crate::__macro_helpers::concat!( "\x01L_OBJC_CLASS_NAME_", $hash, )] static NAME_DATA: [$crate::__macro_helpers::u8; X.len()] = $crate::__statics_string_to_known_length_bytes!(X); /// SAFETY: Same as `REF` above in `__statics_sel!`. #[link_section = "__OBJC,__cls_refs,literal_pointers"] #[export_name = $crate::__macro_helpers::concat!( "\x01L_OBJC_CLASS_REFERENCES_", $hash, )] static REF: $crate::__macro_helpers::SyncUnsafeCell<&$crate::runtime::AnyClass> = unsafe { let ptr: *const $crate::runtime::AnyClass = NAME_DATA.as_ptr().cast(); $crate::__macro_helpers::SyncUnsafeCell::new(&*ptr) }; $crate::__statics_image_info!($hash); $crate::__statics_module_info!($hash); } } #[doc(hidden)] #[macro_export] #[cfg(all( feature = "unstable-static-sel", not(feature = "unstable-static-sel-inlined") ))] macro_rules! __sel_inner { ($data:expr, $hash:expr) => {{ $crate::__statics_sel! { ($data) ($hash) } /// HACK: Wrap the access in a non-generic, `#[inline(never)]` /// function to make the compiler group it into the same codegen unit /// as the statics. /// /// See the following link for details on how the compiler decides /// to partition code into codegen units: /// #[inline(never)] fn objc_static_workaround() -> $crate::runtime::Sel { // SAFETY: The actual selector is replaced by dyld when the // program is loaded. // // This is similar to a volatile read, except it can be stripped // if unused. unsafe { *REF.get() } } objc_static_workaround() }}; } #[doc(hidden)] #[macro_export] #[cfg(feature = "unstable-static-sel-inlined")] macro_rules! __sel_inner { ($data:expr, $hash:expr) => {{ $crate::__statics_sel! { ($data) ($hash) } #[allow(unused_unsafe)] // SAFETY: See above unsafe { *REF.get() } }}; } #[doc(hidden)] #[macro_export] #[cfg(all( feature = "unstable-static-class", not(feature = "gnustep-1-7"), not(feature = "unstable-static-class-inlined") ))] macro_rules! __class_inner { ($name:expr, $hash:expr) => {{ $crate::__statics_class! { ($name) ($hash) } #[inline(never)] fn objc_static_workaround() -> &'static $crate::runtime::AnyClass { // SAFETY: Same as __sel_inner unsafe { *REF.get() } } objc_static_workaround() }}; } #[doc(hidden)] #[macro_export] #[cfg(all( feature = "unstable-static-class", not(feature = "gnustep-1-7"), feature = "unstable-static-class-inlined" ))] macro_rules! __class_inner { ($name:expr, $hash:expr) => {{ $crate::__statics_class! { ($name) ($hash) } #[allow(unused_unsafe)] // SAFETY: See above unsafe { *REF.get() } }}; } #[doc(hidden)] #[macro_export] // The linking changed in libobjc2 v2.0 #[cfg(all(feature = "gnustep-1-7", not(feature = "gnustep-2-0"),))] macro_rules! __class_static_name { ($name:expr) => { $crate::__macro_helpers::concat!("_OBJC_CLASS_", $name) }; } #[doc(hidden)] #[macro_export] #[cfg(feature = "gnustep-2-0")] macro_rules! __class_static_name { ($name:expr) => { $crate::__macro_helpers::concat!("._OBJC_CLASS_", $name) }; } #[doc(hidden)] #[macro_export] #[cfg(all(feature = "unstable-static-class", feature = "gnustep-1-7"))] macro_rules! __class_inner { ($name:expr, $_hash:expr) => {{ // NOTE: This is not verified for correctness in any way whatsoever. extern "C" { #[link_name = $crate::__class_static_name!($name)] static CLASS: $crate::runtime::AnyClass; // Others: // __objc_class_name_ // _OBJC_CLASS_REF_ } #[allow(unused_unsafe)] unsafe { $crate::__macro_helpers::disallow_in_static(&CLASS) } }}; } /// Send a message to an object or class. /// /// This is wildly `unsafe`, even more so than sending messages in /// Objective-C, because this macro can't inspect header files to see the /// expected types, and because Rust has more safety invariants to uphold. /// Make sure to review the safety section below! /// /// The recommended way of using this macro is by defining a wrapper function: /// /// ``` /// # use std::ffi::{c_int, c_char}; /// # use objc2::msg_send; /// # use objc2::runtime::NSObject; /// unsafe fn do_something(obj: &NSObject, arg: c_int) -> *const c_char { /// msg_send![obj, doSomething: arg] /// } /// ``` /// /// This way we are clearly communicating to Rust that: The method /// `doSomething:` works with a shared reference to the object. It takes a /// C-style signed integer, and returns a pointer to what is probably a /// C-compatible string. Now it's much, _much_ easier to make a safe /// abstraction around this! /// /// The [`extern_methods!`] macro can help with coding this pattern. /// /// [`extern_methods!`]: crate::extern_methods /// /// /// # Memory management /// /// If an Objective-C method returns `id`, `NSObject*`, or similar object /// pointers, you should use [`Retained`] on the Rust side, or /// `Option>` if the pointer is nullable. /// /// This is necessary because object pointers in Objective-C have certain /// rules for when they should be retained and released across function calls. /// /// [`Retained`]: crate::rc::Retained /// /// /// ## A little history /// /// Objective-C's type system is... limited, so you can't tell without /// consulting the documentation who is responsible for releasing an object. /// To remedy this problem, Apple/Cocoa introduced (approximately) the /// following rule: /// /// The caller is responsible for releasing objects return from methods that /// begin with `new`, `alloc`, `copy`, `mutableCopy` or `init`, and method /// that begins with `init` takes ownership of the receiver. See [Cocoa's /// Memory Management Policy][mmRules] for a user-friendly introduction to /// this concept. /// /// In the past, users had to do `retain` and `release` calls themselves to /// properly follow these rules. To avoid the memory management problems /// associated with manual stuff like that, they [introduced "ARC"][arc-rel], /// which codifies the rules as part of the language, and inserts the required /// `retain` and `release` calls automatically. /// /// Returning a `*const T` pointer is similar to pre-ARC; you have to know /// when to retain and when to release an object. Returning `Retained` is /// similar to ARC; the rules are simple enough that we can do them /// automatically! /// /// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-SW1 /// [arc-rel]: https://developer.apple.com/library/archive/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226 /// /// /// # Specification /// /// The syntax is somewhat similar to the message syntax in Objective-C, /// except with a comma between arguments. Eliding the comma is possible, but /// deprecated, and may be removed in a future version of `objc2`. /// /// The first expression, know as the "receiver", can be any type that /// implements [`MessageReceiver`], like a reference or a pointer to an /// object. Additionally, it can even be a reference to an [`Retained`] /// containing an object. /// /// The expression can be wrapped in `super`, with an optional superclass /// as the second argument. If no specific superclass is specified, the /// direct superclass is retrieved from [`ClassType`]. /// /// All arguments, as well as the return type, must implement [`Encode`] (bar /// the exceptions below). /// /// If the last argument is the special marker `_`, the macro will return a /// `Result<_, Retained>`, see below. /// /// This macro roughly translates into a call to [`sel!`], and afterwards a /// fully qualified call to [`MessageReceiver::send_message`]. Note that this /// means that auto-dereferencing of the receiver is not supported, and that /// the receiver is consumed. You may encounter a little trouble with `&mut` /// references, try refactoring into a separate method or reborrowing the /// reference. /// /// Variadic arguments are currently not supported. /// /// [`MessageReceiver`]: crate::runtime::MessageReceiver /// [`Retained`]: crate::rc::Retained /// [`ClassType`]: crate::ClassType /// [`Encode`]: crate::Encode /// [`sel!`]: crate::sel /// [`MessageReceiver::send_message`]: crate::runtime::MessageReceiver::send_message /// /// /// ## Memory management details /// /// The accepted receiver and return types, and how we handle them, differ /// depending on which, if any, of the [recognized selector /// families][sel-families] the selector belongs to: /// /// - The `new` family: The receiver may be anything that implements /// [`MessageReceiver`] (though often you'll want to use `&AnyClass`). The /// return type is a generic `Retained` or `Option>`. /// /// - The `alloc` family: The receiver must be `&AnyClass`, and the return /// type is a generic `Allocated`. /// /// - The `init` family: The receiver must be `Allocated` as returned from /// `alloc`, or if sending messages to the superclass, it must be /// `PartialInit`. /// /// The receiver is consumed, and a the now-initialized `Retained` or /// `Option>` (with the same `T`) is returned. /// /// - The `copy` family: The receiver may be anything that implements /// [`MessageReceiver`] and the return type is a generic `Retained` or /// `Option>`. /// /// - The `mutableCopy` family: Same as the `copy` family. /// /// - No family: The receiver may be anything that implements /// [`MessageReceiver`]. The result is retained using /// [`Retained::retain_autoreleased`], and a generic `Retained` or /// `Option>` is returned. This retain is in most cases faster /// than using autorelease pools! /// /// See [the clang documentation][arc-retainable] for the precise /// specification of Objective-C's ownership rules. /// /// As you may have noticed, the return type is usually either `Retained` or /// `Option`. Internally, the return type is always /// `Option` (for example: almost all `new` methods can fail if the /// allocation failed), but for convenience, if the return type is /// `Retained`, this macro will automatically unwrap the object, or panic /// with an error message if it couldn't be retrieved. /// /// As a special case, if the last argument is the marker `_`, the macro will /// return a `Result, Retained>`, see below. /// /// The `retain`, `release` and `autorelease` selectors are not supported, use /// [`Retained::retain`], [`Retained::drop`] and [`Retained::autorelease_ptr`] /// for that. /// /// [sel-families]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families /// [`MessageReceiver`]: crate::runtime::MessageReceiver /// [`Retained::retain_autoreleased`]: crate::rc::Retained::retain_autoreleased /// [arc-retainable]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers-as-operands-and-arguments /// [`Retained::retain`]: crate::rc::Retained::retain /// [`Retained::drop`]: crate::rc::Retained::drop /// [`Retained::autorelease_ptr`]: crate::rc::Retained::autorelease_ptr /// /// /// # `bool` handling /// /// Objective-C's `BOOL` is slightly different from Rust's [`bool`], and hence /// a conversion step must be performed before using it. This is _very_ easy /// to forget (because it'll happen to work in _most_ cases), so this macro /// does the conversion step automatically whenever an argument or the return /// type is `bool`. /// /// That means that any Objective-C method that take or return `BOOL` can be /// translated to use `bool` on the Rust side. /// /// If you want to handle the conversion explicitly, or the Objective-C method /// expects e.g. a pointer to a `BOOL`, use [`runtime::Bool`] instead. /// /// [`runtime::Bool`]: crate::runtime::Bool /// /// /// # Out-parameters /// /// Parameters like `NSString**` in Objective-C are passed by "writeback", /// which means that the callee autoreleases any value that they may write /// into the parameter. /// /// This macro has support for passing such parameters using the following /// types: /// - `&mut Retained<_>` /// - `Option<&mut Retained<_>>` /// - `&mut Option>`, /// - `Option<&mut Option>>` /// /// Beware with the first two, since they will cause undefined behaviour if /// the method overwrites the value with `nil`. /// /// See [clang's documentation][clang-out-params] for more details. /// /// [clang-out-params]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#passing-to-an-out-parameter-by-writeback /// /// /// # Errors /// /// The most common place you'll see out-parameters is as `NSError**` the last /// parameter, which is used to communicate errors to the caller, see [Error /// Handling Programming Guide For Cocoa][cocoa-error]. /// /// Similar to Swift's [importing of error parameters][swift-error], this /// macro supports an even more convenient version than the out-parameter /// support, which transforms methods whose last parameter is `NSError**` into /// the Rust equivalent, the [`Result`] type. /// /// In particular, if you make the last argument the special marker `_`, then /// the macro will return a `Result>`. The error type `E` must /// be either [`NSObject`] or `objc2_foundation::NSError`. /// /// The success type `R` must be either `()` or `Retained`. /// /// At runtime, we create the temporary error variable for you on the stack /// and send it as the out-parameter to the method. If the method then returns /// `NO`/`false`, or in the case of an object pointer, `NULL`, the error /// variable is loaded and returned in [`Err`]. /// /// [cocoa-error]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorHandling/ErrorHandling.html /// [swift-error]: https://developer.apple.com/documentation/swift/about-imported-cocoa-error-parameters /// [`NSObject`]: crate::runtime::NSObject /// /// /// # Panics /// /// Unwinds if the underlying method throws and exception. If the /// `"catch-all"` Cargo feature is enabled, the Objective-C exception is /// converted into a Rust panic, with potentially a bit better stack trace. /// /// Finally, panics if the return type is specified as `Retained<_>`, but the /// method actually returned NULL. If this happens, you should change the /// signature to instead return `Option>` to handle the error /// yourself. /// /// /// ## Type verification /// /// To make message sending safer, all arguments and return values for /// messages must implement [`encode::Encode`]. This allows the Rust compiler /// to prevent you from passing e.g. a [`Vec`] into Objective-C, which would /// both be UB and leak the vector. /// /// When `debug_assertions` are enabled, this macro will check the encoding of /// the given arguments and return every time you send a message, and will /// panic if they are not equivalent. /// /// This is not a perfect solution for ensuring safety (some Rust types have /// the same Objective-C encoding, but are not equivalent, such as `&T` and /// `*const T`), but it gets us much closer to it! /// /// This behaviour can be tweaked with the `"relax-void-encoding"`, /// `"relax-sign-encoding"` or `"disable-encoding-assertions"` Cargo feature /// flags if it is causing you trouble. /// /// [`encode::Encode`]: crate::encode::Encode /// [`Vec`]: std::vec::Vec /// /// /// # Safety /// /// Similar to defining and calling an `extern` function in a foreign function /// interface. In particular, you must uphold the following requirements: /// /// 1. The selector corresponds to a valid method that is available on the /// receiver. /// /// 2. The argument types match what the receiver excepts for this selector. /// /// 3. The return type match what the receiver returns for this selector. /// /// 4. The call must not violate Rust's mutability rules, for example if /// passing an `&T`, the Objective-C method must not mutate the variable /// (except if the variable is inside [`std::cell::UnsafeCell`] or /// derivatives). /// /// 5. If the receiver is a raw pointer it must be valid (aligned, /// dereferenceable, initialized and so on). Messages to `null` pointers /// are allowed (though heavily discouraged), but _only_ if the return type /// itself is a pointer. /// /// 6. You must uphold any additional safety requirements (explicit and /// implicit) that the method has. For example: /// - Methods that take pointers usually require that the pointer is valid, /// and sometimes non-null. /// - Sometimes, a method may only be called on the main thread. /// - The lifetime of returned pointers usually follows certain rules, and /// may not be valid outside of an [`autoreleasepool`] (returning /// `Retained` usually helps with these cases). /// /// 7. Each out-parameter must have the correct nullability, and the method /// must not have any attributes that changes the how it handles memory /// management for these. /// /// 8. If using the automatic memory management facilities of this macro, the /// method must not have any attributes such as `objc_method_family`, /// `ns_returns_retained`, `ns_consumed` that changes the how it handles /// memory management. /// /// 8. TODO: Maybe more? /// /// [`autoreleasepool`]: crate::rc::autoreleasepool /// /// /// # Examples /// /// Interacting with [`NSURLComponents`], [`NSString`] and [`NSNumber`]. /// /// [`NSURLComponents`]: https://developer.apple.com/documentation/foundation/nsurlcomponents?language=objc /// [`NSString`]: https://developer.apple.com/documentation/foundation/nsstring?language=objc /// [`NSNumber`]: https://developer.apple.com/documentation/foundation/nsnumber?language=objc /// /// ``` /// use objc2::rc::Retained; /// use objc2::{msg_send, ClassType}; /// use objc2_foundation::{NSNumber, NSString, NSURLComponents}; /// /// /// // Create an empty `NSURLComponents` by calling the class method `new`. /// let components: Retained = unsafe { /// // ^^^^^^^^^^^^^^^^^^^^^^^^^ the return type, a memory-managed /// // `NSURLComponents` instance /// // /// msg_send![NSURLComponents::class(), new] /// // ------------------------ ^^^ the selector `new` /// // | /// // the receiver, in this case the class itself /// }; /// /// /// // Create a new `NSNumber` from an integer. /// let port: Retained = unsafe { /// msg_send![NSNumber::class(), numberWithInt: 8080i32] /// // -------------- ^^^^^^^ the argument to the method /// // | /// // the selector `numberWithInt:` /// // /// // Note how we must fully specify the argument as `8080i32` instead of just `8080`. /// }; /// /// /// // Set the port property of the URL. /// let _: () = unsafe { msg_send![&components, setPort: &*port] }; /// // -- -------- ^^^^^^ the port is deref'd to /// // | | become the correct type /// // | | /// // | the selector `setPort:` is derived /// // | from the property name `port`. /// // | /// // return type (i.e. nothing / void) /// // /// // Note that even return types of `void` must be explicitly specified as `()`. /// /// /// // Set the `host` property of the URL. /// let host: Retained = unsafe { /// msg_send![NSString::class(), stringWithUTF8String: c"example.com".as_ptr()] /// }; /// let _: () = unsafe { msg_send![&components, setHost: &*host] }; /// /// /// // Set the `scheme` property of the URL. /// let scheme: Retained = unsafe { /// msg_send![NSString::class(), stringWithUTF8String: c"http".as_ptr()] /// }; /// let _: () = unsafe { msg_send![&components, setScheme: &*scheme] }; /// /// /// // Get the combined URL in string form. /// let string: Option> = unsafe { msg_send![&components, string] }; /// // ^^^^^^ the method can return NULL, so we specify an option here /// /// /// assert_eq!(string.unwrap().to_string(), "http://example.com:8080"); /// ``` /// /// The example above uses only `msg_send!` for demonstration purposes; note /// that usually the interface you seek is already present in [the framework /// crates] and then the equivalent code can be as simple as: /// /// [the framework crates]: crate::topics::about_generated /// /// ``` /// use objc2_foundation::{NSNumber, NSString, NSURLComponents}; /// /// let components = unsafe { NSURLComponents::new() }; /// unsafe { components.setPort(Some(&NSNumber::new_i32(8080))) }; /// unsafe { components.setHost(Some(&NSString::from_str("example.com"))) }; /// unsafe { components.setScheme(Some(&NSString::from_str("http"))) }; /// let string = unsafe { components.string() }; /// /// assert_eq!(string.unwrap().to_string(), "http://example.com:8080"); /// ``` /// /// Sending messages to the superclass of an object. /// /// ```no_run /// use objc2::runtime::NSObject; /// use objc2::{msg_send, ClassType}; /// # /// # objc2::define_class!( /// # #[unsafe(super(NSObject))] /// # struct MyObject; /// # ); /// # /// # let obj: objc2::rc::Retained = todo!(); /// /// // Call `someMethod` on the direct super class. /// let _: () = unsafe { msg_send![super(&obj), someMethod] }; /// /// // Or lower-level, a method on a specific superclass. /// let superclass = NSObject::class(); /// let arg3: u32 = unsafe { msg_send![super(&obj, superclass), getArg3] }; /// ``` /// /// Sending a message with automatic error handling. /// /// ```no_run /// use objc2::msg_send; /// use objc2::rc::Retained; /// # #[cfg(requires_foundation)] /// use objc2_foundation::{NSBundle, NSError}; /// # use objc2::runtime::NSObject as NSBundle; /// # use objc2::runtime::NSObject as NSError; /// /// # #[cfg(requires_foundation)] /// let bundle = NSBundle::mainBundle(); /// # let bundle = NSBundle::new(); /// /// let res: Result<(), Retained> = unsafe { /// // -- -------- ^^^^^^^ must be NSError or NSObject /// // | | /// // | always retained /// // | /// // `()` means that the method returns `bool`, we check /// // that and return success if `true`, an error if `false` /// // /// msg_send![&bundle, preflightAndReturnError: _] /// // ^ activate error handling /// }; /// ``` /// /// Sending a message with an out parameter _and_ automatic error handling. /// /// ```no_run /// use objc2::msg_send; /// use objc2::rc::Retained; /// /// # type NSFileManager = objc2::runtime::NSObject; /// # type NSURL = objc2::runtime::NSObject; /// # type NSError = objc2::runtime::NSObject; /// let obj: &NSFileManager; /// # obj = todo!(); /// let url: &NSURL; /// # url = todo!(); /// let mut result_url: Option> = None; /// unsafe { /// msg_send![ /// obj, /// trashItemAtURL: url, /// resultingItemURL: Some(&mut result_url), /// error: _ /// ]? /// // ^ is possible on error-returning methods, if the return type is specified /// }; /// /// // Use `result_url` here /// /// # Ok::<(), Retained>(()) /// ``` /// /// Attempt to do an invalid message send. This is undefined behaviour, but /// will panic with `debug_assertions` enabled. /// /// ```should_panic /// use objc2::msg_send; /// use objc2::runtime::NSObject; /// /// let obj = NSObject::new(); /// /// // Wrong return type - this is UB! /// // /// // But it will be caught with `debug_assertions` enabled, stating that /// // the return type's encoding is not correct. /// let hash: f32 = unsafe { msg_send![&obj, hash] }; /// # /// # panic!("does not panic in release mode, so for testing we make it!"); /// ``` #[macro_export] macro_rules! msg_send { [super($obj:expr), $($selector_and_arguments:tt)+] => { $crate::__msg_send_parse! { () () ($($selector_and_arguments)+) (MsgSendSuperError::send_super_message_static_error) (MsgSendSuper::send_super_message_static) ($crate::__msg_send_helper) ($obj) () // No method family } }; [super($obj:expr, $superclass:expr), $($selector_and_arguments:tt)+] => { $crate::__msg_send_parse! { () () ($($selector_and_arguments)+) (MsgSendSuperError::send_super_message_error) (MsgSendSuper::send_super_message) ($crate::__msg_send_helper) ($obj, $superclass) () // No method family } }; [$obj:expr, $($selector_and_arguments:tt)+] => { $crate::__msg_send_parse! { () () ($($selector_and_arguments)+) (MsgSendError::send_message_error) (MsgSend::send_message) ($crate::__msg_send_helper) ($obj) () // No method family } }; } /// Use [`msg_send!`] instead, it now supports converting to/from `bool`. #[macro_export] #[deprecated = "use a normal msg_send! instead, it will perform the conversion for you"] macro_rules! msg_send_bool { [$($msg_send_args:tt)+] => ({ // Use old impl for backwards compat let result: $crate::runtime::Bool = $crate::msg_send![$($msg_send_args)+]; result.as_bool() }); } /// Use [`msg_send!`] instead, it now supports converting to/from /// [`Retained`][crate::rc::Retained]. #[macro_export] #[deprecated = "use a normal msg_send! instead, it will now perform the conversion to/from `Retained` for you"] macro_rules! msg_send_id { [$($msg_send_args:tt)+] => { $crate::msg_send![$($msg_send_args)*] } } #[doc(hidden)] #[macro_export] macro_rules! __msg_send_helper { { ($($fn_args:tt)+) ($($method_family:tt)*) ($trait:ident :: $fn:ident) ($($selector:tt)*) ($($argument:expr,)*) } => ({ // Assign to intermediary variable for better UI, and to prevent // miscompilation on older Rust versions (TODO: Which ones?). // // Note: This can be accessed from any expression in `fn_args` and // `argument` - we won't (yet) bother with preventing that though. let result; // Always add trailing comma after each argument, so that we get a // 1-tuple if there is only one. // // And use `::<_, _>` for better UI. result = <$crate::__method_family!(($($method_family)*) ($($selector)*)) as $crate::__macro_helpers::$trait<_, _>>::$fn( $($fn_args)+, $crate::sel!($($selector)*), ($($argument,)*), ); result }); }