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)
)
}};
}
/// 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! __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. letmut res: [$crate::__macro_helpers::u8; $inp.len()] = [0; $inp.len()]; letmut 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();
};
}
/// 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());
};
}
/// 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)] staticREF: $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! /// <https://github.com/rust-lang/rust/issues/29603> /// <https://stackoverflow.com/a/16936512> /// <http://sealiesoftware.com/blog/archive/2010/4/8/Do-it-yourself_Objective-C_weak_import.html> #[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,
)] staticREF: $crate::__macro_helpers::SyncUnsafeCell<&$crate::runtime::AnyClass> = unsafe {
$crate::__macro_helpers::SyncUnsafeCell::new(&CLASS)
};
/// 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: /// <https://doc.rust-lang.org/1.61.0/nightly-rustc/rustc_monomorphize/partitioning/index.html> #[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() }
}
#[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;
/// 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<T>`] on the Rust side, or /// `Option<Retained<T>>` 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<T>`]: 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<E>>`, 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<T>` or `Option<Retained<T>>`. /// /// - The `alloc` family: The receiver must be `&AnyClass`, and the return /// type is a generic `Allocated<T>`. /// /// - The `init` family: The receiver must be `Allocated<T>` as returned from /// `alloc`, or if sending messages to the superclass, it must be /// `PartialInit<T>`. /// /// The receiver is consumed, and a the now-initialized `Retained<T>` or /// `Option<Retained<T>>` (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<T>` or /// `Option<Retained<T>>`. /// /// - 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<T>` or /// `Option<Retained<T>>` 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<Retained>`. Internally, the return type is always /// `Option<Retained>` (for example: almost all `new` methods can fail if the /// allocation failed), but for convenience, if the return type is /// `Retained<T>`, 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<T>, Retained<E>>`, 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<Retained<_>>`, /// - `Option<&mut Option<Retained<_>>>` /// /// 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<R, Retained<E>>`. The error type `E` must /// be either [`NSObject`] or `objc2_foundation::NSError`. /// /// The success type `R` must be either `()` or `Retained<T>`. /// /// 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<Retained<_>>` 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<NSURLComponents> = 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<NSNumber> = 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<NSString> = 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<NSString> = 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<Retained<NSString>> = 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<MyObject> = 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<NSError>> = 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<Retained<NSURL>> = 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<NSError>>(()) /// ``` /// /// 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)+)
($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
});
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.56Bemerkung:
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.