|
3 | 3 | //! Classes can be declared using the [`ClassBuilder`] struct. Instance
|
4 | 4 | //! variables and methods can then be added before the class is ultimately
|
5 | 5 | //! registered.
|
6 |
| -//! |
7 |
| -//! **Note**: You likely don't need the dynamicism that this module provides! |
8 |
| -//! Consider using the [`declare_class!`][crate::declare_class] macro instead. |
9 |
| -//! |
10 |
| -//! |
11 |
| -//! ## Example |
12 |
| -//! |
13 |
| -//! The following example demonstrates declaring a class named `MyNumber` that |
14 |
| -//! has one ivar, a `u32` named `_number` and a few methods for constructor |
15 |
| -//! methods and methods for interfacing with the number (using interior |
16 |
| -//! mutability, as is common for Objective-C objects). |
17 |
| -//! |
18 |
| -//! ``` |
19 |
| -//! use core::cell::Cell; |
20 |
| -//! |
21 |
| -//! use objc2::declare::ClassBuilder; |
22 |
| -//! use objc2::rc::Id; |
23 |
| -//! use objc2::runtime::{AnyClass, AnyObject, NSObject, Sel}; |
24 |
| -//! use objc2::{sel, msg_send, msg_send_id, ClassType}; |
25 |
| -//! |
26 |
| -//! fn register_class() -> &'static AnyClass { |
27 |
| -//! // Inherit from NSObject |
28 |
| -//! let mut builder = ClassBuilder::new("MyNumber", NSObject::class()) |
29 |
| -//! .expect("a class with the name MyNumber likely already exists"); |
30 |
| -//! |
31 |
| -//! // Add an instance variable of type `Cell<u32>` |
32 |
| -//! builder.add_ivar::<Cell<u32>>("_number"); |
33 |
| -//! |
34 |
| -//! // Add an Objective-C method for initializing an instance with a number |
35 |
| -//! // |
36 |
| -//! // We "cheat" a bit here, and use `AnyObject` instead of `NSObject`, |
37 |
| -//! // since only the former is allowed to be a mutable receiver (which is |
38 |
| -//! // always safe in `init` methods, but not in others). |
39 |
| -//! unsafe extern "C" fn init_with_number( |
40 |
| -//! this: &mut AnyObject, |
41 |
| -//! _cmd: Sel, |
42 |
| -//! number: u32, |
43 |
| -//! ) -> Option<&mut AnyObject> { |
44 |
| -//! let this: Option<&mut AnyObject> = msg_send![super(this, NSObject::class()), init]; |
45 |
| -//! this.map(|this| { |
46 |
| -//! // SAFETY: The ivar is added with the same type above |
47 |
| -//! this.set_ivar::<Cell<u32>>("_number", Cell::new(number)); |
48 |
| -//! this |
49 |
| -//! }) |
50 |
| -//! } |
51 |
| -//! unsafe { |
52 |
| -//! builder.add_method( |
53 |
| -//! sel!(initWithNumber:), |
54 |
| -//! init_with_number as unsafe extern "C" fn(_, _, _) -> _, |
55 |
| -//! ); |
56 |
| -//! } |
57 |
| -//! |
58 |
| -//! // Add convenience method for getting a new instance with the number |
59 |
| -//! extern "C" fn with_number( |
60 |
| -//! cls: &AnyClass, |
61 |
| -//! _cmd: Sel, |
62 |
| -//! number: u32, |
63 |
| -//! ) -> *mut NSObject { |
64 |
| -//! let obj: Option<Id<NSObject>> = unsafe { |
65 |
| -//! msg_send_id![ |
66 |
| -//! msg_send_id![cls, alloc], |
67 |
| -//! initWithNumber: number, |
68 |
| -//! ] |
69 |
| -//! }; |
70 |
| -//! obj.map(Id::autorelease_return).unwrap_or(std::ptr::null_mut()) |
71 |
| -//! } |
72 |
| -//! unsafe { |
73 |
| -//! builder.add_class_method( |
74 |
| -//! sel!(withNumber:), |
75 |
| -//! with_number as extern "C" fn(_, _, _) -> _, |
76 |
| -//! ); |
77 |
| -//! } |
78 |
| -//! |
79 |
| -//! // Add an Objective-C method for setting the number |
80 |
| -//! extern "C" fn my_number_set(this: &NSObject, _cmd: Sel, number: u32) { |
81 |
| -//! // SAFETY: The ivar is added with the same type above |
82 |
| -//! unsafe { this.ivar::<Cell<u32>>("_number") }.set(number); |
83 |
| -//! } |
84 |
| -//! unsafe { |
85 |
| -//! builder.add_method(sel!(setNumber:), my_number_set as extern "C" fn(_, _, _)); |
86 |
| -//! } |
87 |
| -//! |
88 |
| -//! // Add an Objective-C method for getting the number |
89 |
| -//! extern "C" fn my_number_get(this: &NSObject, _cmd: Sel) -> u32 { |
90 |
| -//! // SAFETY: The ivar is added with the same type above |
91 |
| -//! unsafe { this.ivar::<Cell<u32>>("_number") }.get() |
92 |
| -//! } |
93 |
| -//! unsafe { |
94 |
| -//! builder.add_method(sel!(number), my_number_get as extern "C" fn(_, _) -> _); |
95 |
| -//! } |
96 |
| -//! |
97 |
| -//! builder.register() |
98 |
| -//! } |
99 |
| -//! |
100 |
| -//! // Usage |
101 |
| -//! |
102 |
| -//! // Note: you should only do class registration once! This can be ensure |
103 |
| -//! // with `std::sync::Once` or the `once_cell` crate. |
104 |
| -//! let cls = register_class(); |
105 |
| -//! |
106 |
| -//! let obj: Id<NSObject> = unsafe { |
107 |
| -//! msg_send_id![cls, withNumber: 42u32] |
108 |
| -//! }; |
109 |
| -//! |
110 |
| -//! let n: u32 = unsafe { msg_send![&obj, number] }; |
111 |
| -//! assert_eq!(n, 42); |
112 |
| -//! |
113 |
| -//! let _: () = unsafe { msg_send![&obj, setNumber: 12u32] }; |
114 |
| -//! let n: u32 = unsafe { msg_send![&obj, number] }; |
115 |
| -//! assert_eq!(n, 12); |
116 |
| -//! ``` |
117 | 6 |
|
118 | 7 | mod ivar;
|
119 | 8 | mod ivar_bool;
|
@@ -335,6 +224,116 @@ impl<T> Log2Alignment for T {
|
335 | 224 |
|
336 | 225 | /// A type for declaring a new class and adding new methods and ivars to it
|
337 | 226 | /// before registering it.
|
| 227 | +/// |
| 228 | +/// **Note**: You likely don't need the dynamicism that this provides! |
| 229 | +/// Consider using the [`declare_class!`][crate::declare_class] macro instead. |
| 230 | +/// |
| 231 | +/// |
| 232 | +/// # Example |
| 233 | +/// |
| 234 | +/// Declare a class named `MyNumber` that has one ivar, a `u32` named `_number` |
| 235 | +/// and a few constructor methods and methods for interfacing with the number |
| 236 | +/// (using interior mutability, as is common for Objective-C objects). |
| 237 | +/// |
| 238 | +/// ``` |
| 239 | +/// use core::cell::Cell; |
| 240 | +/// |
| 241 | +/// use objc2::declare::ClassBuilder; |
| 242 | +/// use objc2::rc::Id; |
| 243 | +/// use objc2::runtime::{AnyClass, AnyObject, NSObject, Sel}; |
| 244 | +/// use objc2::{sel, msg_send, msg_send_id, ClassType}; |
| 245 | +/// |
| 246 | +/// fn register_class() -> &'static AnyClass { |
| 247 | +/// // Inherit from NSObject |
| 248 | +/// let mut builder = ClassBuilder::new("MyNumber", NSObject::class()) |
| 249 | +/// .expect("a class with the name MyNumber likely already exists"); |
| 250 | +/// |
| 251 | +/// // Add an instance variable of type `Cell<u32>` |
| 252 | +/// builder.add_ivar::<Cell<u32>>("_number"); |
| 253 | +/// |
| 254 | +/// // Add an Objective-C method for initializing an instance with a number |
| 255 | +/// // |
| 256 | +/// // We "cheat" a bit here, and use `AnyObject` instead of `NSObject`, |
| 257 | +/// // since only the former is allowed to be a mutable receiver (which is |
| 258 | +/// // always safe in `init` methods, but not in others). |
| 259 | +/// unsafe extern "C" fn init_with_number( |
| 260 | +/// this: &mut AnyObject, |
| 261 | +/// _cmd: Sel, |
| 262 | +/// number: u32, |
| 263 | +/// ) -> Option<&mut AnyObject> { |
| 264 | +/// let this: Option<&mut AnyObject> = msg_send![super(this, NSObject::class()), init]; |
| 265 | +/// this.map(|this| { |
| 266 | +/// // SAFETY: The ivar is added with the same type above |
| 267 | +/// this.set_ivar::<Cell<u32>>("_number", Cell::new(number)); |
| 268 | +/// this |
| 269 | +/// }) |
| 270 | +/// } |
| 271 | +/// unsafe { |
| 272 | +/// builder.add_method( |
| 273 | +/// sel!(initWithNumber:), |
| 274 | +/// init_with_number as unsafe extern "C" fn(_, _, _) -> _, |
| 275 | +/// ); |
| 276 | +/// } |
| 277 | +/// |
| 278 | +/// // Add convenience method for getting a new instance with the number |
| 279 | +/// extern "C" fn with_number( |
| 280 | +/// cls: &AnyClass, |
| 281 | +/// _cmd: Sel, |
| 282 | +/// number: u32, |
| 283 | +/// ) -> *mut NSObject { |
| 284 | +/// let obj: Option<Id<NSObject>> = unsafe { |
| 285 | +/// msg_send_id![ |
| 286 | +/// msg_send_id![cls, alloc], |
| 287 | +/// initWithNumber: number, |
| 288 | +/// ] |
| 289 | +/// }; |
| 290 | +/// obj.map(Id::autorelease_return).unwrap_or(std::ptr::null_mut()) |
| 291 | +/// } |
| 292 | +/// unsafe { |
| 293 | +/// builder.add_class_method( |
| 294 | +/// sel!(withNumber:), |
| 295 | +/// with_number as extern "C" fn(_, _, _) -> _, |
| 296 | +/// ); |
| 297 | +/// } |
| 298 | +/// |
| 299 | +/// // Add an Objective-C method for setting the number |
| 300 | +/// extern "C" fn my_number_set(this: &NSObject, _cmd: Sel, number: u32) { |
| 301 | +/// // SAFETY: The ivar is added with the same type above |
| 302 | +/// unsafe { this.ivar::<Cell<u32>>("_number") }.set(number); |
| 303 | +/// } |
| 304 | +/// unsafe { |
| 305 | +/// builder.add_method(sel!(setNumber:), my_number_set as extern "C" fn(_, _, _)); |
| 306 | +/// } |
| 307 | +/// |
| 308 | +/// // Add an Objective-C method for getting the number |
| 309 | +/// extern "C" fn my_number_get(this: &NSObject, _cmd: Sel) -> u32 { |
| 310 | +/// // SAFETY: The ivar is added with the same type above |
| 311 | +/// unsafe { this.ivar::<Cell<u32>>("_number") }.get() |
| 312 | +/// } |
| 313 | +/// unsafe { |
| 314 | +/// builder.add_method(sel!(number), my_number_get as extern "C" fn(_, _) -> _); |
| 315 | +/// } |
| 316 | +/// |
| 317 | +/// builder.register() |
| 318 | +/// } |
| 319 | +/// |
| 320 | +/// // Usage |
| 321 | +/// |
| 322 | +/// // Note: you should only do class registration once! This can be ensured |
| 323 | +/// // with `std::sync::Once` or the `once_cell` crate. |
| 324 | +/// let cls = register_class(); |
| 325 | +/// |
| 326 | +/// let obj: Id<NSObject> = unsafe { |
| 327 | +/// msg_send_id![cls, withNumber: 42u32] |
| 328 | +/// }; |
| 329 | +/// |
| 330 | +/// let n: u32 = unsafe { msg_send![&obj, number] }; |
| 331 | +/// assert_eq!(n, 42); |
| 332 | +/// |
| 333 | +/// let _: () = unsafe { msg_send![&obj, setNumber: 12u32] }; |
| 334 | +/// let n: u32 = unsafe { msg_send![&obj, number] }; |
| 335 | +/// assert_eq!(n, 12); |
| 336 | +/// ``` |
338 | 337 | #[derive(Debug)]
|
339 | 338 | pub struct ClassBuilder {
|
340 | 339 | // Note: Don't ever construct a &mut objc_class, since it is possible to
|
|
0 commit comments