Skip to content

Commit 39a6d52

Browse files
committed
Extract internal CachedSel and CachedClass types to simplify macros
1 parent 3569a1b commit 39a6d52

File tree

3 files changed

+86
-46
lines changed

3 files changed

+86
-46
lines changed

src/cache.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use std::os::raw::c_void;
2+
use std::ptr;
3+
use std::sync::atomic::{AtomicPtr, Ordering};
4+
5+
use crate::runtime::{Class, Sel, self};
6+
7+
/// Allows storing a `Sel` in a static and lazily loading it.
8+
#[doc(hidden)]
9+
pub struct CachedSel {
10+
ptr: AtomicPtr<c_void>
11+
}
12+
13+
impl CachedSel {
14+
/// Constructs a new `CachedSel`.
15+
pub const fn new() -> CachedSel {
16+
CachedSel {
17+
ptr: AtomicPtr::new(ptr::null_mut())
18+
}
19+
}
20+
21+
/// Returns the cached selector. If no selector is yet cached, registers
22+
/// one with the given name and stores it.
23+
#[inline(always)]
24+
pub unsafe fn get(&self, name: &str) -> Sel {
25+
let ptr = self.ptr.load(Ordering::Relaxed);
26+
// It should be fine to use `Relaxed` ordering here because `sel_registerName` is
27+
// thread-safe.
28+
if ptr.is_null() {
29+
let sel = runtime::sel_registerName(name.as_ptr() as *const _);
30+
self.ptr.store(sel.as_ptr() as *mut _, Ordering::Relaxed);
31+
sel
32+
} else {
33+
Sel::from_ptr(ptr)
34+
}
35+
}
36+
}
37+
38+
/// Allows storing a `Class` reference in a static and lazily loading it.
39+
#[doc(hidden)]
40+
pub struct CachedClass {
41+
ptr: AtomicPtr<Class>
42+
}
43+
44+
impl CachedClass {
45+
/// Constructs a new `CachedClass`.
46+
pub const fn new() -> CachedClass {
47+
CachedClass {
48+
ptr: AtomicPtr::new(ptr::null_mut())
49+
}
50+
}
51+
52+
/// Returns the cached class. If no class is yet cached, gets one with
53+
/// the given name and stores it.
54+
#[inline(always)]
55+
pub unsafe fn get(&self, name: &str) -> Option<&'static Class> {
56+
// `Relaxed` should be fine since `objc_getClass` is thread-safe.
57+
let ptr = self.ptr.load(Ordering::Relaxed);
58+
if ptr.is_null() {
59+
let cls = runtime::objc_getClass(name.as_ptr() as *const _);
60+
self.ptr.store(cls as *mut _, Ordering::Relaxed);
61+
cls.as_ref()
62+
} else {
63+
Some(&*ptr)
64+
}
65+
}
66+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ pub use objc_encode::{Encode, Encoding};
7575
pub use crate::encode::EncodeArguments;
7676
pub use crate::message::{Message, MessageArguments, MessageError};
7777

78+
pub use crate::cache::CachedClass as __CachedClass;
79+
pub use crate::cache::CachedSel as __CachedSel;
7880
pub use crate::message::send_message as __send_message;
7981
pub use crate::message::send_super_message as __send_super_message;
8082

@@ -84,6 +86,7 @@ mod macros;
8486
pub mod runtime;
8587
pub mod declare;
8688
pub mod rc;
89+
mod cache;
8790
mod encode;
8891
#[cfg(feature = "exception")]
8992
mod exception;

src/macros.rs

Lines changed: 17 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,56 +15,17 @@ let cls = class!(NSObject);
1515
#[macro_export]
1616
macro_rules! class {
1717
($name:ident) => ({
18-
#[allow(deprecated)]
19-
#[inline(always)]
20-
fn get_class(name: &str) -> Option<&'static $crate::runtime::Class> {
21-
unsafe {
22-
static CLASS: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(0);
23-
// `Relaxed` should be fine since `objc_getClass` is thread-safe.
24-
let ptr = CLASS.load(::std::sync::atomic::Ordering::Relaxed) as *const $crate::runtime::Class;
25-
if ptr.is_null() {
26-
let cls = $crate::runtime::objc_getClass(name.as_ptr() as *const _);
27-
CLASS.store(cls as usize, ::std::sync::atomic::Ordering::Relaxed);
28-
if cls.is_null() { None } else { Some(&*cls) }
29-
} else {
30-
Some(&*ptr)
31-
}
32-
}
33-
}
34-
match get_class(concat!(stringify!($name), '\0')) {
18+
static CLASS: $crate::__CachedClass = $crate::__CachedClass::new();
19+
let name = concat!(stringify!($name), '\0');
20+
#[allow(unused_unsafe)]
21+
let cls = unsafe { CLASS.get(name) };
22+
match cls {
3523
Some(cls) => cls,
3624
None => panic!("Class with name {} could not be found", stringify!($name)),
3725
}
3826
})
3927
}
4028

41-
#[doc(hidden)]
42-
#[macro_export]
43-
macro_rules! sel_impl {
44-
// Declare a function to hide unsafety, otherwise we can trigger the
45-
// unused_unsafe lint; see rust-lang/rust#8472
46-
($name:expr) => ({
47-
#[allow(deprecated)]
48-
#[inline(always)]
49-
fn register_sel(name: &str) -> $crate::runtime::Sel {
50-
unsafe {
51-
static SEL: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(0);
52-
let ptr = SEL.load(::std::sync::atomic::Ordering::Relaxed) as *const ::std::os::raw::c_void;
53-
// It should be fine to use `Relaxed` ordering here because `sel_registerName` is
54-
// thread-safe.
55-
if ptr.is_null() {
56-
let sel = $crate::runtime::sel_registerName(name.as_ptr() as *const _);
57-
SEL.store(sel.as_ptr() as usize, ::std::sync::atomic::Ordering::Relaxed);
58-
sel
59-
} else {
60-
$crate::runtime::Sel::from_ptr(ptr)
61-
}
62-
}
63-
}
64-
register_sel($name)
65-
})
66-
}
67-
6829
/**
6930
Registers a selector, returning a `Sel`.
7031
@@ -79,8 +40,18 @@ let sel = sel!(setObject:forKey:);
7940
*/
8041
#[macro_export]
8142
macro_rules! sel {
82-
($name:ident) => ({$crate::sel_impl!(concat!(stringify!($name), '\0'))});
83-
($($name:ident :)+) => ({$crate::sel_impl!(concat!($(stringify!($name), ':'),+, '\0'))});
43+
($name:ident) => ({
44+
static SEL: $crate::__CachedSel = $crate::__CachedSel::new();
45+
let name = concat!(stringify!($name), '\0');
46+
#[allow(unused_unsafe)]
47+
unsafe { SEL.get(name) }
48+
});
49+
($($name:ident :)+) => ({
50+
static SEL: $crate::__CachedSel = $crate::__CachedSel::new();
51+
let name = concat!($(stringify!($name), ':'),+, '\0');
52+
#[allow(unused_unsafe)]
53+
unsafe { SEL.get(name) }
54+
});
8455
}
8556

8657
/**

0 commit comments

Comments
 (0)