/// Create a new trait to represent a protocol. /// /// This is similar to a `@protocol` declaration in Objective-C. /// /// See [Protocols - The Objective-C Programming Language][protocols] and /// [Working with Protocols - Programming with Objective-C][working-with] for /// general information about protocols in Objective-C. /// /// This macro will create an `unsafe` trait with methods that provide access /// to the functionality exposed by the protocol. /// /// Conforming to the protocol can be done in two ways: /// - For external classes, use the [`extern_conformance!`] macro. /// - For custom classes created with the [`define_class!`] macro, implement /// the trait inside the macro. /// /// Objective-C has a smart feature where you can write `id<MyProtocol>`, and /// then work with the protocol as-if it was an object; this is very similar /// to `dyn` traits in Rust, and we implement it in a similar way, see /// [`ProtocolObject`] for details. /// /// [protocols]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html /// [working-with]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithProtocols/WorkingwithProtocols.html /// [`extern_conformance!`]: crate::extern_conformance /// [`ProtocolObject`]: crate::runtime::ProtocolObject /// /// /// # Specification /// /// The syntax is similar enough to Rust syntax that if you invoke the macro /// with parentheses (as opposed to curly brackets), `rustfmt` will be able to /// format the contents. /// /// This macro creates an `unsafe` trait with the specified methods. A default /// implementation of the method is generated based on the selector specified /// with `#[unsafe(method(a:selector:))]`. Similar to [`extern_methods!`], you /// can use the `#[unsafe(method_family = ...)]` attribute to override the /// inferred method family. /// /// Other protocols that this protocol conforms to / inherits can be specified /// as supertraits. /// /// The new trait `T` is automatically implemented for /// [`ProtocolObject<dyn T>`], which also means that [`ProtocolType`] is /// implemented for `dyn T`. /// /// Finally, you can use the `#[optional]` attribute to mark optional methods. /// This currently doesn't have any effect, but probably will have one in the /// future when implementing protocols in [`define_class!`]. /// /// This macro otherwise shares similarities with [`extern_class!`] and /// [`extern_methods!`]. /// /// [`ProtocolObject<dyn T>`]: crate::runtime::ProtocolObject /// [`ProtocolType`]: crate::ProtocolType /// [`define_class!`]: crate::define_class /// [`extern_class!`]: crate::extern_class /// [`extern_methods!`]: crate::extern_methods /// /// /// # Safety /// /// The following are required for using the macro itself: /// - The specified name must be an existing Objective-C protocol. /// - The protocol must actually inherit/conform to the protocols specified /// as supertraits. /// /// Each method is annotated with `#[unsafe(method(...))]`, where you are /// responsible for ensuring that the declaration is correct. /// /// While the following are required when implementing the `unsafe` trait for /// a new type: /// - The type must represent an object that implements the protocol. /// /// /// # Examples /// /// Create a trait to represent the `NSItemProviderWriting` protocol (in /// practice, you would import this from `objc2-foundation`, this is just for /// demonstration purposes). /// /// ``` /// use std::ffi::c_void; /// use objc2::ffi::NSInteger; /// use objc2::rc::Retained; /// use objc2::runtime::{NSObject, NSObjectProtocol}; /// use objc2::extern_protocol; /// # type NSArray<T> = T; /// # type NSString = NSObject; /// # type NSProgress = NSObject; /// # type NSItemProviderRepresentationVisibility = NSInteger; /// # #[cfg(defined_in_foundation)] /// use objc2_foundation::{NSArray, NSString, NSProgress, NSItemProviderRepresentationVisibility}; /// /// extern_protocol!( /// /// This comment will appear on the trait as expected. /// // /// // We could have named the trait something else on the Rust-side, and /// // then used this to keep it correct from Objective-C. /// // #[name = "NSItemProviderWriting"] /// // /// // SAFETY: /// // - The name is correct. /// // - The protocol does inherit from `NSObjectProtocol`. /// pub unsafe trait NSItemProviderWriting: NSObjectProtocol { /// // ^^^^^^^^^^^^^^^^ /// // This protocol inherits from the `NSObject` protocol /// /// // This method we mark as `unsafe`, since we aren't using the /// // correct type for the completion handler. /// #[unsafe(method(loadDataWithTypeIdentifier:forItemProviderCompletionHandler:))] /// unsafe fn loadData( /// &self, /// type_identifier: &NSString, /// completion_handler: *mut c_void, /// ) -> Option<Retained<NSProgress>>; /// /// // SAFETY: The method is correctly specified. /// #[unsafe(method(writableTypeIdentifiersForItemProvider))] /// fn writableTypeIdentifiersForItemProvider_class() /// -> Retained<NSArray<NSString>>; /// /// // The rest of these are optional, which means that a user of /// // `define_class!` would not need to implement them. /// /// // SAFETY: The method is correctly specified. /// #[unsafe(method(writableTypeIdentifiersForItemProvider))] /// #[optional] /// fn writableTypeIdentifiersForItemProvider(&self) /// -> Retained<NSArray<NSString>>; /// /// // SAFETY: The method is correctly specified. /// #[unsafe(method(itemProviderVisibilityForRepresentationWithTypeIdentifier:))] /// #[optional] /// fn itemProviderVisibilityForRepresentation_class( /// type_identifier: &NSString, /// ) -> NSItemProviderRepresentationVisibility; /// /// // SAFETY: The method is correctly specified. /// #[unsafe(method(itemProviderVisibilityForRepresentationWithTypeIdentifier:))] /// #[optional] /// fn itemProviderVisibilityForRepresentation( /// &self, /// type_identifier: &NSString, /// ) -> NSItemProviderRepresentationVisibility; /// } /// ); /// /// // Types can now implement `NSItemProviderWriting`, and use the methods /// // from it as we specified. /// ``` /// /// See the source code of `objc2-foundation` for many more examples. #[doc(alias = "@protocol")] #[macro_export]
macro_rules! extern_protocol {
( // The special #[name = $name:literal] attribute is supported here.
$(#[$($attrs:tt)*])*
$v:vis unsafetrait $protocol:ident $(: $conforms_to:ident $(+ $conforms_to_rest:ident)*)? {
$($methods:tt)*
}
) => {
$crate::__extract_struct_attributes! {
($(#[$($attrs)*])*)
$($attr_impl)* unsafeimpl<T> $protocol for $crate::runtime::ProtocolObject<T> where
T: ?$crate::__macro_helpers::Sized + $protocol
{}
// SAFETY: The specified name is ensured by caller to be a protocol, // and is correctly defined.
$($attr_impl)* unsafeimpl $crate::ProtocolType fordyn $protocol { const NAME: &'static $crate::__macro_helpers::str = $crate::__fallback_if_not_set! {
($($name)*)
($crate::__macro_helpers::stringify!($protocol))
}; const __INNER: () = ();
}
// SAFETY: Anything that implements the protocol is valid to convert // to `ProtocolObject<dyn [PROTO]>`.
$($attr_impl)* unsafeimpl<T> $crate::runtime::ImplementedBy<T> fordyn $protocol where
T: ?$crate::__macro_helpers::Sized + $crate::Message + $protocol
{ const __INNER: () = ();
}
// TODO: Should we also implement `ImplementedBy` for `Send + Sync` // types, as is done for `NSObjectProtocol`?
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.