/// Allows storing a [`Sel`] in a static and lazily loading it. #[derive(Debug)] pubstruct CachedSel {
ptr: AtomicPtr<c_void>,
}
impl CachedSel { /// Constructs a new [`CachedSel`]. #[allow(clippy::new_without_default)] pubconstfn new() -> Self { Self {
ptr: AtomicPtr::new(ptr::null_mut()),
}
}
// Mark as cold since this should only ever be called once (or maybe twice // if running on multiple threads). #[cold] unsafefn fetch(&self, name: *const c_char) -> Sel { // 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) }; self.ptr.store(sel.as_ptr() as *mut _, Ordering::Relaxed);
sel
}
/// Returns the cached selector. If no selector is yet cached, registers /// one with the given name and stores it. #[inline] pubunsafefn get(&self, name: &str) -> Sel { // `Relaxed` should be fine since `sel_registerName` is thread-safe. let ptr = self.ptr.load(Ordering::Relaxed); iflet Some(sel) = unsafe { Sel::from_ptr(ptr) } {
sel
} else { // SAFETY: Checked by caller unsafe { self.fetch(name.as_ptr().cast()) }
}
}
}
/// Allows storing a [`AnyClass`] reference in a static and lazily loading it. #[derive(Debug)] pubstruct CachedClass {
ptr: AtomicPtr<AnyClass>,
}
impl CachedClass { /// Constructs a new [`CachedClass`]. #[allow(clippy::new_without_default)] pubconstfn new() -> CachedClass {
CachedClass {
ptr: AtomicPtr::new(ptr::null_mut()),
}
}
// Mark as cold since this should only ever be called once (or maybe twice // if running on multiple threads). #[cold] #[track_caller] unsafefn fetch(&self, name: *const c_char) -> &'static AnyClass { let ptr: *const AnyClass = unsafe { ffi::objc_getClass(name) }.cast(); self.ptr.store(ptr as *mut AnyClass, Ordering::Relaxed); iflet Some(cls) = unsafe { ptr.as_ref() } {
cls
} else { // Recover the name from the pointer. We do it like this so that // we don't have to pass the length of the class to this method, // improving binary size. let name = unsafe { CStr::from_ptr(name) }; let name = str::from_utf8(name.to_bytes()).unwrap();
panic!("class {name} could not be found")
}
}
/// Returns the cached class. If no class is yet cached, gets one with /// the given name and stores it. #[inline] #[track_caller] pubunsafefn get(&self, name: &str) -> &'static AnyClass { // `Relaxed` should be fine since `objc_getClass` is thread-safe. let ptr = self.ptr.load(Ordering::Relaxed); iflet Some(cls) = unsafe { ptr.as_ref() } {
cls
} else { // SAFETY: Checked by caller unsafe { self.fetch(name.as_ptr().cast()) }
}
}
}
#[cfg(test)] mod tests { #[test] #[should_panic = "class NonExistentClass could not be found"] #[cfg(not(feature = "unstable-static-class"))] fn test_not_found() { let _ = crate::class!(NonExistentClass);
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.25 Sekunden
(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.