1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
use crate::ffi;
use crate::runtime::{Class, Sel};
/// Allows storing a [`Sel`] in a static and lazily loading it.
#[doc(hidden)]
pub struct CachedSel {
ptr: AtomicPtr<ffi::objc_selector>,
}
impl CachedSel {
/// Constructs a new [`CachedSel`].
pub const fn new() -> CachedSel {
CachedSel {
ptr: AtomicPtr::new(ptr::null_mut()),
}
}
/// Returns the cached selector. If no selector is yet cached, registers
/// one with the given name and stores it.
#[inline]
#[doc(hidden)]
pub unsafe fn get(&self, name: &str) -> Sel {
// `Relaxed` should be fine since `sel_registerName` is thread-safe.
let ptr = self.ptr.load(Ordering::Relaxed);
unsafe { Sel::from_ptr(ptr) }.unwrap_or_else(|| {
// The panic inside `Sel::register_unchecked` is unfortunate, but
// strict correctness is more important than speed
// SAFETY: Input is a non-null, NUL-terminated C-string pointer.
//
// We know this, because we construct it in `sel!` ourselves
let sel = unsafe { Sel::register_unchecked(name.as_ptr().cast()) };
self.ptr
.store(sel.as_ptr() as *mut ffi::objc_selector, Ordering::Relaxed);
sel
})
}
}
/// Allows storing a [`Class`] reference in a static and lazily loading it.
#[doc(hidden)]
pub struct CachedClass {
ptr: AtomicPtr<Class>,
}
impl CachedClass {
/// Constructs a new [`CachedClass`].
pub const fn new() -> CachedClass {
CachedClass {
ptr: AtomicPtr::new(ptr::null_mut()),
}
}
/// Returns the cached class. If no class is yet cached, gets one with
/// the given name and stores it.
#[inline]
#[doc(hidden)]
pub unsafe fn get(&self, name: &str) -> Option<&'static Class> {
// `Relaxed` should be fine since `objc_getClass` is thread-safe.
let ptr = self.ptr.load(Ordering::Relaxed);
if let Some(cls) = unsafe { ptr.as_ref() } {
Some(cls)
} else {
let ptr: *const Class = unsafe { ffi::objc_getClass(name.as_ptr().cast()) }.cast();
self.ptr.store(ptr as *mut Class, Ordering::Relaxed);
unsafe { ptr.as_ref() }
}
}
}