1#[cfg(all(
2 target_arch = "aarch64",
3 not(target_pointer_width = "32"),
4 not(feature = "classic-objc-retain-release")
5))]
6use std::arch::asm;
7use std::{borrow::Cow, ffi::c_void, marker::PhantomData, ptr::NonNull};
8
9use crate::{arc, cf::Type, objc};
10
11#[derive(Debug)]
12#[repr(transparent)]
13pub struct Class<T: Obj>(Type, PhantomData<T>);
14
15#[derive(Debug)]
16#[repr(transparent)]
17pub struct Protocol(Type);
18
19impl<T: Obj> Class<T> {
20 pub unsafe fn method_impl(&self, name: &Sel) -> *const c_void {
21 unsafe { class_getMethodImplementation(std::mem::transmute(self), name) }
22 }
23
24 pub unsafe fn add_protocol(&self, protocol: &Protocol) -> bool {
25 unsafe { class_addProtocol(std::mem::transmute(self), protocol) }
26 }
27}
28
29#[derive(Debug)]
30#[repr(transparent)]
31pub struct ClassInstExtra<T: Obj, I: Sized>(Class<T>, PhantomData<I>);
32
33impl<T: Obj, I: Sized> std::ops::Deref for ClassInstExtra<T, I> {
34 type Target = Class<T>;
35
36 fn deref(&self) -> &Self::Target {
37 &self.0
38 }
39}
40
41pub const NS_OBJECT_SIZE: usize = std::mem::size_of::<usize>();
43
44#[macro_export]
45macro_rules! init_with_default {
46 ($NewType:ty, $InnerType:ty) => {{
47 trait A {
48 fn init_fn(&self) -> Option<extern "C" fn()>;
49 }
50
51 struct B<T: ?Sized>(core::marker::PhantomData<T>);
52
53 impl<T: ?Sized> core::ops::Deref for B<T> {
54 type Target = ();
55 fn deref(&self) -> &Self::Target {
56 &()
57 }
58 }
59
60 impl<T: ?Sized> A for B<T>
61 where
62 T: Default,
63 {
64 fn init_fn(&self) -> Option<extern "C" fn()> {
65 extern "C" fn impl_init<T: Default>(
66 s: *mut $NewType,
67 _sel: Option<$crate::objc::Sel>,
68 ) -> $crate::arc::R<$NewType> {
69 unsafe {
70 let ptr: *mut u8 = s.cast();
71 let d_ptr: *mut std::mem::ManuallyDrop<T> =
72 ptr.add($crate::objc::NS_OBJECT_SIZE) as _;
73 *d_ptr = std::mem::ManuallyDrop::new(T::default());
74
75 std::mem::transmute(ptr)
76 }
77 }
78
79 let ptr = unsafe { std::mem::transmute(impl_init::<T> as *const u8) };
80 Some(ptr)
81 }
82 }
83
84 impl A for () {
85 fn init_fn(&self) -> Option<extern "C" fn()> {
86 None
87 }
88 }
89
90 B::<$InnerType>(core::marker::PhantomData).init_fn()
91 }};
92}
93
94impl<T: Obj, I: Sized> ClassInstExtra<T, I> {
95 #[inline]
96 pub fn alloc_init(&self, var: I) -> arc::R<T> {
97 unsafe {
98 let inst = class_createInstance(std::mem::transmute(self), std::mem::size_of::<I>());
99
100 let ptr: *mut u8 = std::mem::transmute(inst);
104 let d_ptr: *mut std::mem::ManuallyDrop<I> = ptr.add(NS_OBJECT_SIZE) as _;
105 *d_ptr = std::mem::ManuallyDrop::new(var);
106
107 std::mem::transmute(ptr)
108 }
109 }
110}
111
112impl<T: Obj, I: Sized + Default> ClassInstExtra<T, I> {
113 pub fn new(&self) -> arc::R<T> {
114 self.alloc_init(Default::default())
115 }
116}
117
118impl<T: Obj> Class<T> {
119 #[inline]
120 pub fn as_type_ref(&self) -> &Type {
121 &self.0
122 }
123
124 #[must_use]
125 #[objc::msg_send(alloc)]
126 pub fn alloc(&self) -> arc::A<T>;
127
128 #[objc::msg_send(new)]
130 pub unsafe fn new(&self) -> arc::Retained<T>;
131}
132
133impl<T: Obj> Obj for Class<T> {}
134
135impl<T: Obj> arc::Release for T {
136 #[inline]
137 unsafe fn release(&mut self) {
138 unsafe { <T as Obj>::release(self) }
139 }
140}
141
142impl<T: Obj> arc::Retain for T {
143 fn retained(&self) -> arc::R<Self> {
144 unsafe { Self::retain(self) }
145 }
146}
147
148pub trait Obj: Sized + arc::Retain {
149 #[inline]
150 unsafe fn retain(id: &Self) -> arc::R<Self> {
151 unsafe {
152 #[cfg(all(target_arch = "aarch64", not(feature = "classic-objc-retain-release")))]
153 {
154 let result: *mut Self;
155 core::arch::asm!(
156 "bl _objc_retain_{obj:x}",
157 obj = in(reg) id,
158 lateout("x0") result,
159 out("x16") _,
160 out("x17") _,
161 out("x30") _,
162 clobber_abi("C"),
163 );
164 std::mem::transmute(result)
165 }
166
167 #[cfg(any(target_arch = "x86_64", feature = "classic-objc-retain-release"))]
168 {
169 std::mem::transmute(objc_retain(std::mem::transmute(id)))
170 }
171 }
172 }
173
174 #[inline]
175 unsafe fn release(id: &mut Self) {
176 unsafe {
177 #[cfg(all(
178 target_arch = "aarch64",
179 target_pointer_width = "64",
180 not(feature = "classic-objc-retain-release")
181 ))]
182 {
183 asm!(
184 "bl _objc_release_{x}",
185 x = in(reg) id,
186 out("x16") _,
187 out("x17") _,
188 out("x30") _,
189 clobber_abi("C")
190 );
193 }
194
195 #[cfg(any(
196 target_arch = "x86_64",
197 target_pointer_width = "32",
198 feature = "classic-objc-retain-release"
199 ))]
200 {
201 objc_release(std::mem::transmute(id));
202 }
203 }
204 }
205
206 #[objc::msg_send(description)]
207 fn desc(&self) -> arc::R<crate::ns::String>;
208
209 #[objc::msg_send(debugDescription)]
210 fn debug_desc(&self) -> arc::R<crate::ns::String>;
211
212 #[objc::msg_send(respondsToSelector:)]
213 fn responds_to_sel(&self, sel: &Sel) -> bool;
214
215 #[objc::msg_send(class)]
216 fn class(&self) -> &crate::objc::Class<Self>;
217
218 #[objc::msg_send(isKindOfClass:)]
219 fn is_kind_of_class<T: Obj>(&self, cls: &crate::objc::Class<T>) -> bool;
220
221 #[inline]
222 fn try_cast<T: Obj>(&self, cls: &crate::objc::Class<T>) -> Option<&T> {
223 if self.is_kind_of_class(cls) {
224 Some(unsafe { std::mem::transmute(self) })
225 } else {
226 None
227 }
228 }
229
230 #[inline]
231 fn try_cast_mut<T: Obj>(&mut self, cls: &crate::objc::Class<T>) -> Option<&mut T> {
232 if self.is_kind_of_class(cls) {
233 Some(unsafe { std::mem::transmute(self) })
234 } else {
235 None
236 }
237 }
238
239 #[objc::msg_send(isMemberOfClass:)]
240 fn is_member_of_class<T: Obj>(&self, cls: &crate::objc::Class<T>) -> bool;
241
242 #[cfg(not(target_os = "watchos"))]
243 #[inline]
244 fn is_tagged_ptr(&self) -> bool {
245 ((self as *const Self as usize) >> 63) == 1
246 }
247
248 #[inline]
249 fn as_id_ref(&self) -> &Id {
250 unsafe { std::mem::transmute(self) }
251 }
252}
253
254#[repr(transparent)]
256pub struct Id(Type);
257
258unsafe impl Send for Id {}
259
260impl Id {
261 #[inline]
262 pub unsafe fn autorelease<'ar>(id: &mut Id) -> &'ar mut Id {
263 unsafe { objc_autorelease(id) }
264 }
265
266 #[inline]
272 pub fn as_type_ref(&self) -> &Type {
273 &self.0
274 }
275
276 #[inline]
277 pub fn as_id_ref(&self) -> &Self {
278 self
279 }
280
281 #[objc::msg_send(isEqual:)]
282 pub fn is_equal(&self, other: &Self) -> bool;
283
284 #[objc::msg_send(hash)]
285 pub fn hash(&self) -> ns::UInteger;
286
287 pub fn as_ptr(&self) -> *const Self {
288 self as *const Self
289 }
290}
291
292impl Obj for Id {}
293
294impl std::fmt::Debug for Id {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 let desc = self.debug_desc();
297 f.write_str(&Cow::from(desc.as_cf()))
298 }
300}
301
302#[derive(Debug)]
303#[repr(transparent)]
304pub struct Sel(NonNull<c_void>);
305
306pub mod autorelease_pool;
307pub mod ns;
308pub use autorelease_pool::AutoreleasePoolPage;
309
310pub fn ar_pool<R, F>(f: F) -> R
311where
312 F: FnOnce() -> R,
313 R: Clone, {
315 let _page = AutoreleasePoolPage::push();
316 f()
317}
318
319pub unsafe fn sel_reg_name(str: *const i8) -> &'static Sel {
320 unsafe { std::mem::transmute(sel_registerName(str)) }
321}
322
323#[doc(alias = "objc_super")]
324#[repr(C)]
325#[derive(Debug, Copy, Clone)]
326pub struct Super {
327 pub receiver: *mut Id,
328 pub super_class: *const Class<Id>,
329}
330
331#[link(name = "objc", kind = "dylib")]
332unsafe extern "C-unwind" {
333 #[cfg(any(target_arch = "x86_64", feature = "classic-objc-retain-release"))]
334 pub fn objc_retain<'a>(obj: &Id) -> &'a Id;
335 #[cfg(any(
336 target_arch = "x86_64",
337 target_pointer_width = "32",
338 feature = "classic-objc-retain-release"
339 ))]
340 fn objc_release(obj: &mut Id);
341
342 pub fn class_createInstance(cls: &Class<Id>, extra_bytes: usize) -> arc::A<Id>;
345 fn class_getMethodImplementation(cls: &Class<Id>, name: &Sel) -> *const c_void;
346 fn class_addProtocol(cls: &Class<Id>, protocol: &Protocol) -> bool;
347 fn objc_autorelease<'ar>(id: &mut Id) -> &'ar mut Id;
348
349 pub fn objc_retainAutoreleasedReturnValue<'ar>(obj: Option<&Id>) -> Option<arc::R<Id>>;
350 pub fn objc_retainAutoreleaseReturnValue<'ar>(obj: Option<&Id>) -> Option<&'ar Id>;
351 pub fn objc_claimAutoreleasedReturnValue() -> Option<arc::R<Id>>;
352 pub fn objc_autoreleaseReturnValue<'ar>(obj: Option<&Id>) -> Option<&'ar Id>;
353
354 pub fn objc_copyWeak<'ar>(dest: *mut *mut Id, src: *mut *mut Id) -> Option<&'ar Id>;
355 pub fn objc_destroyWeak(location: *mut *mut Id);
356 pub fn objc_storeWeak<'ar>(location: *mut *mut Id, value: Option<&Id>) -> Option<&'ar Id>;
357 pub fn objc_loadWeakRetained(location: *mut *mut Id) -> Option<arc::R<Id>>;
358
359 pub fn object_getIndexedIvars(obj: *const c_void) -> *mut c_void;
360 pub fn sel_registerName(str: *const i8) -> *const std::ffi::c_void;
361 pub fn class_addMethod(
362 cls: &Class<Id>,
363 name: &Sel,
364 imp: extern "C" fn(),
365 types: *const u8,
366 ) -> bool;
367
368 pub fn class_replaceMethod(
369 cls: &Class<Id>,
370 name: &Sel,
371 imp: extern "C" fn(),
372 types: *const u8,
373 ) -> Option<extern "C" fn()>;
374
375 pub fn objc_allocateClassPair(
376 super_cls: &Class<Id>,
377 name: *const u8,
378 extra_bytes: usize,
379 ) -> Option<&'static Class<Id>>;
380 pub fn object_getClass(obj: Option<&Id>) -> Option<&Class<Id>>;
381 pub fn objc_registerClassPair(cls: &Class<Id>);
382 pub fn objc_getClass(name: *const u8) -> Option<&'static Class<Id>>;
383 pub fn objc_getProtocol(name: *const i8) -> Option<&'static Protocol>;
384 pub fn objc_msgSendSuper(s: &Super, sel: &Sel);
385 pub static NS_OBJECT: &'static crate::objc::Class<Id>;
386 fn objc_exception_throw(exception: &Id) -> !;
387}
388
389#[macro_export]
391macro_rules! define_cls_init {
392 ($NewType:ident, $CLS:ident) => {
393 impl $crate::arc::A<$NewType> {
394 #[$crate::objc::msg_send(init)]
395 pub fn init(self) -> arc::Retained<$NewType>;
396 }
397
398 impl $NewType {
399 $crate::define_cls!($CLS);
400
401 #[inline]
403 pub fn new() -> $crate::arc::R<$NewType> {
404 Self::alloc().init()
405 }
406 }
407 };
408}
409
410#[macro_export]
411macro_rules! define_weak_cls_init {
412 ($NewType:ident, $CLS:ident) => {
413 impl $crate::arc::A<$NewType> {
414 #[$crate::objc::msg_send(init)]
415 pub fn init(self) -> arc::Retained<$NewType>;
416 }
417
418 impl $NewType {
419 $crate::define_weak_cls!($CLS);
420
421 #[inline]
423 pub fn new() -> Option<$crate::arc::R<$NewType>> {
424 Some(Self::alloc()?.init())
425 }
426 }
427 };
428}
429
430#[macro_export]
436macro_rules! define_cls {
437 ($CLS:ident) => {
438 #[inline]
439 pub fn cls() -> &'static $crate::objc::Class<Self> {
440 unsafe { std::mem::transmute($CLS) }
441 }
442
443 #[inline]
444 pub fn cls_ptr() -> *const std::ffi::c_void {
445 unsafe { std::mem::transmute($CLS) }
446 }
447
448 #[inline]
449 pub fn alloc() -> $crate::arc::A<Self> {
450 Self::cls().alloc()
451 }
452 };
453}
454
455#[macro_export]
456macro_rules! define_weak_cls {
457 ($CLS:ident) => {
458 #[inline]
459 pub fn cls() -> Option<&'static $crate::objc::Class<Self>> {
460 unsafe { std::mem::transmute($CLS) }
461 }
462
463 #[inline]
464 pub fn cls_ptr() -> *const std::ffi::c_void {
465 unsafe { std::mem::transmute($CLS) }
466 }
467
468 #[inline]
469 pub fn alloc() -> Option<$crate::arc::A<Self>> {
470 Some(Self::cls()?.alloc())
471 }
472 };
473}
474
475#[macro_export]
476macro_rules! define_obj_type {
477 (
478 $(#[$outer:meta])*
479 $vis:vis
480 $NewType:ident $(+ $TraitImpl:path)*, $InnerType:path, $CLS:ident) => {
481 $crate::define_obj_type!(
482 $(#[$outer])*
483 $vis
484 $NewType(objc::Id)
485 );
486
487 impl $NewType {
488 #[allow(dead_code)]
489 #[inline]
490 pub fn inner(&self) -> &$InnerType {
491 unsafe {
492 let ptr = self as *const Self as *const u8;
493 let ptr = ptr.add($crate::objc::NS_OBJECT_SIZE);
494 &*(ptr as *const $InnerType)
495 }
496 }
497
498 #[allow(dead_code)]
499 #[inline]
500 pub fn inner_mut(&mut self) -> &mut $InnerType {
501 unsafe {
502 let ptr: *mut u8 = self as *mut Self as *mut u8;
503 let ptr = ptr.add($crate::objc::NS_OBJECT_SIZE);
504 &mut *(ptr as *mut $InnerType)
505 }
506 }
507
508 #[allow(dead_code)]
509 pub fn register_cls() -> &'static $crate::objc::ClassInstExtra<Self, $InnerType> {
510 let name = concat!(stringify!($CLS), "\0");
511 let cls = unsafe { $crate::objc::objc_allocateClassPair($crate::objc::NS_OBJECT, name.as_ptr(), 0) };
512 let cls = cls.unwrap();
513 $(<Self as $TraitImpl>::cls_add_methods(cls);)*
514 $(<Self as $TraitImpl>::cls_add_protocol(cls);)*
515
516 if let Some(init_fn_ptr) = $crate::init_with_default!($NewType, $InnerType) {
517 unsafe {
518 let sel = $crate::objc::sel_reg_name(c"init".as_ptr() as _);
519 let imp: extern "C" fn() = init_fn_ptr;
520 $crate::objc::class_addMethod(cls, sel, imp, std::ptr::null());
521
522 let sel = $crate::objc::sel_reg_name(c"alloc".as_ptr() as _);
523 let meta_cls = $crate::objc::object_getClass(Some(std::mem::transmute(cls))).unwrap();
524
525 extern "C" fn alloc_impl(cls: &$crate::objc::Class<$crate::ns::Id>) -> $crate::arc::A<$NewType> {
526 unsafe {
527 let inst = $crate::objc::class_createInstance(cls, std::mem::size_of::<$InnerType>());
528 std::mem::transmute(inst)
529 }
530
531 }
532
533
534 $crate::objc::class_addMethod(meta_cls, sel, std::mem::transmute(alloc_impl as *const u8), std::ptr::null());
535
536 }
537 }
538
539 if std::mem::needs_drop::<$InnerType>() {
540 extern "C" fn impl_dealloc(s: &mut $NewType, sel: &$crate::objc::Sel) {
541 let ptr = s.inner_mut() as *mut _;
542 unsafe {
543 std::ptr::drop_in_place(ptr);
544 let sup = $crate::objc::Super {
545 receiver: std::mem::transmute(s),
546 super_class: $crate::objc::NS_OBJECT
547 };
548 $crate::objc::objc_msgSendSuper(&sup, sel);
549 }
550 }
551 unsafe {
552 let sel = $crate::objc::sel_reg_name(c"dealloc".as_ptr() as _);
553 let imp: extern "C" fn() = std::mem::transmute(impl_dealloc as *const u8);
554 $crate::objc::class_addMethod(cls, sel, imp, std::ptr::null());
555 }
556 }
557 unsafe { $crate::objc::objc_registerClassPair(cls) };
558 unsafe { std::mem::transmute(cls) }
559 }
560
561 #[allow(dead_code)]
562 pub fn cls() -> &'static $crate::objc::ClassInstExtra<Self, $InnerType> {
563 let name = concat!(stringify!($CLS), "\0");
564 let cls = unsafe { $crate::objc::objc_getClass(name.as_ptr()) };
565 match cls {
566 Some(c) => unsafe { std::mem::transmute(c) }
567 None => Self::register_cls()
568 }
569 }
570
571 #[allow(dead_code)]
572 #[inline]
573 pub fn cls_ptr() -> *const std::ffi::c_void {
574 Self::cls() as *const $crate::objc::ClassInstExtra<Self, $InnerType> as *const std::ffi::c_void
575 }
576
577 #[allow(dead_code)]
578 pub fn with(inner: $InnerType) -> $crate::arc::R<Self> {
579 Self::cls().alloc_init(inner)
580 }
581 }
582 };
583 (
584 $(#[$outer:meta])*
585 $vis:vis
586 $NewType:ident $(+ $TraitImpl:path)*, (), $CLS:ident) => {
587 $crate::define_obj_type!(
588 $(#[$outer])*
589 $vis
590 $NewType(objc::Id)
591 );
592
593 impl $NewType {
594
595 #[allow(dead_code)]
596 pub fn register_cls() -> &'static $crate::objc::ClassInstExtra<Self, ()> {
597 let name = concat!(stringify!($CLS), "\0");
598 let cls = unsafe { $crate::objc::objc_allocateClassPair($crate::objc::NS_OBJECT, name.as_ptr(), 0) };
599 let cls = cls.unwrap();
600 $(<Self as $TraitImpl>::cls_add_methods(cls);)*
601 $(<Self as $TraitImpl>::cls_add_protocol(cls);)*
602
603 unsafe { $crate::objc::objc_registerClassPair(cls) };
604 unsafe { std::mem::transmute(cls) }
605 }
606
607 #[allow(dead_code)]
608 pub fn cls() -> &'static $crate::objc::ClassInstExtra<Self, ()> {
609 let name = concat!(stringify!($CLS), "\0");
610 let cls = unsafe { $crate::objc::objc_getClass(name.as_ptr()) };
611 match cls {
612 Some(c) => unsafe { std::mem::transmute(c) }
613 None => Self::register_cls()
614 }
615 }
616
617 #[allow(dead_code)]
618 pub fn new() -> $crate::arc::R<Self> {
619 unsafe { Self::cls().new() }
620 }
621 }
622 };
623 (
624 $(#[$outer:meta])*
625 $vis:vis
626 $NewType:ident($BaseType:path)
627 ) => {
628 $(#[$outer])*
629 #[derive(Debug, PartialEq)]
630 #[repr(transparent)]
631 $vis struct $NewType($BaseType);
632
633 impl $crate::objc::Obj for $NewType {}
634
635 impl std::ops::Deref for $NewType {
636 type Target = $BaseType;
637
638 #[inline]
639 fn deref(&self) -> &Self::Target {
640 &self.0
641 }
642 }
643
644 impl std::ops::DerefMut for $NewType {
645 #[inline]
646 fn deref_mut(&mut self) -> &mut Self::Target {
647 &mut self.0
648 }
649 }
650
651 impl $NewType {
652 #[allow(dead_code)]
653 #[inline]
654 pub fn retained(&self) -> $crate::arc::R<Self> {
655 unsafe { $crate::objc::Obj::retain(self) }
656 }
657 }
658
659 impl PartialEq<$crate::arc::R<$NewType>> for $NewType {
660 fn eq(&self, other: &$crate::arc::R<$NewType>) -> bool {
661 self.0.is_equal(other)
662 }
663 }
664
665 impl AsRef<$crate::ns::Id> for $NewType {
666 fn as_ref(&self) -> &$crate::ns::Id {
667 self
668 }
669 }
670
671 };
678 (
679 $(#[$outer:meta])*
680 $vis:vis
681 $NewType:ident($BaseType:path), $CLS:ident
682 $(, #[$api_available:meta])?
683 ) => {
684 $crate::define_obj_type!(
685 $(#[$outer])*
686 $vis$
687 NewType($BaseType)
688 );
689 $(#[$api_available])?
690 $crate::define_cls_init!($NewType, $CLS);
691 };
692}
693
694impl PartialEq for Id {
695 #[inline]
696 fn eq(&self, other: &Self) -> bool {
697 self.is_equal(other)
698 }
699}
700
701impl Eq for Id {}
702impl std::hash::Hash for Id {
703 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
704 self.hash().hash(state);
705 }
706}
707
708impl Eq for arc::R<Id> {}
709impl std::hash::Hash for arc::R<Id> {
710 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
711 self.0.hash().hash(state);
712 }
713}
714
715#[inline]
718pub fn throw(obj: &Id) -> ! {
719 #[cfg(not(target_os = "watchos"))]
722 debug_assert!(!obj.is_tagged_ptr());
723 unsafe { objc_exception_throw(obj) }
724}
725
726#[link(name = "ns", kind = "static")]
727unsafe extern "C-unwind" {
728 fn cidre_try_catch<'ar>(
729 during: extern "C" fn(ctx: *mut c_void),
730 ctx: *mut c_void,
731 ) -> Option<&'ar Id>;
732}
733
734pub fn try_catch<'ar, F, R>(f: F) -> Result<R, &'ar Id>
735where
736 F: FnOnce() -> R,
737{
738 let mut result = None;
739 let mut wrapper = Some(|| {
740 result = Some(f());
741 });
742
743 let f = type_helper(&wrapper);
744 let ctx = &mut wrapper as *mut _ as *mut c_void;
745
746 unsafe {
747 match cidre_try_catch(std::mem::transmute(f), ctx) {
748 None => Ok(result.unwrap_unchecked()),
749 Some(e) => Err(e),
750 }
751 }
752}
753
754#[inline]
755fn type_helper<F>(_t: &Option<F>) -> extern "C-unwind" fn(t: &mut Option<F>)
756where
757 F: FnOnce(),
758{
759 extern "C-unwind" fn during<F>(f: &mut Option<F>)
760 where
761 F: FnOnce(),
762 {
763 unsafe { f.take().unwrap_unchecked()() };
764 }
765 during
766}
767
768#[cfg(target_arch = "aarch64")]
769#[cfg(test)]
770mod tests {
771
772 use super::ar_pool;
773 use crate::{arc, cf, dispatch, return_ar};
774 use std;
775
776 fn autorelease_example_ar() -> arc::Rar<dispatch::Queue> {
777 let q = dispatch::Queue::new();
778 return_ar!(q)
779 }
780
781 #[test]
782 fn autorelease() {
783 let ptr = ar_pool(|| {
784 let q = autorelease_example_ar().retained();
785 assert_eq!(2, q.as_type_ref().retain_count());
786 unsafe { q.as_type_ref().as_type_ptr() }
787 });
788
789 let _ptr: &cf::Type = unsafe { std::mem::transmute(ptr) };
790 }
791}
792pub use cidre_macros::add_methods;
793pub use cidre_macros::api_available as available;
794pub use cidre_macros::optional;
795pub use cidre_macros::protocol;
796
797#[cfg(target_arch = "aarch64")]
799pub use cidre_macros::msg_send;
800#[cfg(target_arch = "aarch64")]
801pub use cidre_macros::msg_send_debug;
802#[cfg(target_arch = "x86_64")]
803pub use cidre_macros::msg_send_x86_64 as msg_send;
804
805#[cfg(test)]
806mod tests2 {
807
808 use std::collections::HashMap;
809
810 use crate::{
811 arc::{self, Retain},
812 ns,
813 objc::{self, Obj},
814 return_rar,
815 };
816
817 #[objc::protocol(Foo)]
818 trait Foo: objc::Obj {
819 #[objc::msg_send(count)]
820 fn count(&self) -> usize;
821
822 #[objc::msg_send(newObj)]
823 fn new_obj(&self) -> arc::R<ns::String>;
824
825 #[objc::msg_send(prop)]
826 fn prop(&self) -> arc::R<ns::String>;
827
828 #[objc::optional]
829 #[objc::msg_send(count2)]
830 fn count2(&self) -> usize;
831
832 fn direct_fn(&self);
833 }
834
835 static mut DROP_CALLED: bool = false;
836
837 pub struct D;
838
839 impl Drop for D {
840 fn drop(&mut self) {
841 unsafe {
842 DROP_CALLED = true;
843 }
844 }
845 }
846
847 define_obj_type!(Bla + FooImpl, D, BLA_USIZE);
848
849 impl Foo for Bla {
850 fn direct_fn(&self) {}
851 }
852
853 #[objc::add_methods]
854 impl FooImpl for Bla {
855 extern "C" fn impl_count(&self, _cmd: Option<&objc::Sel>) -> usize {
856 0
857 }
858
859 extern "C" fn impl_new_obj(&self, _cmd: Option<&objc::Sel>) -> arc::R<ns::String> {
860 ns::String::new()
861 }
862
863 extern "C" fn impl_prop_ar(&self, _cmd: Option<&objc::Sel>) -> arc::Rar<ns::String> {
864 let s = ns::str!(c"test");
865 return_rar!(s)
866 }
867 }
868
869 #[test]
870 fn basics() {
871 unsafe {
872 DROP_CALLED = false;
873 }
874 {
875 let d = Bla::with(D);
876 assert_eq!(d.prop().to_string(), "test");
877 let _r = d.retained();
878 let desc = d.desc();
879 assert!(desc.to_string().starts_with("<BLA_USIZE: "));
880 }
881 assert!(unsafe { DROP_CALLED });
882 }
883
884 #[test]
885 fn hash() {
886 fn foo() -> HashMap<arc::R<ns::Id>, arc::R<ns::String>> {
887 let a = ns::String::new();
888 let b = ns::String::new();
889 let mut map = HashMap::new();
890 let _v = map.insert(a.as_id_ref().retained(), b);
891 map
892 }
893
894 foo();
895 }
896}