Skip to content

Commit

Permalink
Add a new class! macro that caches classes
Browse files Browse the repository at this point in the history
This macro should improve performance and ergonomics. Like the sel!
macro, it will cache the gotten class so that repeated calls need not
call `objc_getClass`.

The macro only accepts an identifier (not a string), so the class being
looked up is static, and therefore it seems reasonable for this macro to
panic if the requested class does not exist. This improves ergonomics
because users need not call `unwrap` on the result (like was usually
done with `Class::get`).

This fixes #65.
  • Loading branch information
SSheldon committed Jul 7, 2018
1 parent 90ff608 commit 6584f53
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 8 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Objective-C Runtime bindings and wrapper for Rust.
Objective-C objects can be messaged using the `msg_send!` macro:

``` rust
let cls = Class::get("NSObject").unwrap();
let cls = class!(NSObject);
let obj: *mut Object = msg_send![cls, new];
let hash: usize = msg_send![obj, hash];
let is_kind: BOOL = msg_send![obj, isKindOfClass:cls];
Expand All @@ -25,7 +25,7 @@ The following example demonstrates declaring a class named `MyNumber` that has
one ivar, a `u32` named `_number` and a `number` method that returns it:

``` rust
let superclass = Class::get("NSObject").unwrap();
let superclass = class!(NSObject);
let mut decl = ClassDecl::new("MyNumber", superclass).unwrap();

// Add an instance variable
Expand Down
2 changes: 1 addition & 1 deletion examples/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl Drop for StrongPtr {

fn main() {
// Get a class
let cls = Class::get("NSObject").unwrap();
let cls = class!(NSObject);
println!("NSObject size: {}", cls.instance_size());

// Inspect its ivars
Expand Down
2 changes: 1 addition & 1 deletion src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ one ivar, a `u32` named `_number` and a `number` method that returns it:
# use objc::declare::ClassDecl;
# use objc::runtime::{Class, Object, Sel};
# fn main() {
let superclass = Class::get("NSObject").unwrap();
let superclass = class!(NSObject);
let mut decl = ClassDecl::new("MyNumber", superclass).unwrap();
// Add an instance variable
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Objective-C objects can be messaged using the [`msg_send!`](macro.msg_send!.html
# use objc::runtime::{BOOL, Class, Object};
# fn main() {
# unsafe {
let cls = Class::get("NSObject").unwrap();
let cls = class!(NSObject);
let obj: *mut Object = msg_send![cls, new];
let hash: usize = msg_send![obj, hash];
let is_kind: BOOL = msg_send![obj, isKindOfClass:cls];
Expand Down
40 changes: 40 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
/**
Gets a reference to a `Class`.
Panics if no class with the given name can be found.
To check for a class that may not exist, use `Class::get`.
# Example
``` no_run
# #[macro_use] extern crate objc;
# fn main() {
let cls = class!(NSObject);
# }
```
*/
#[macro_export]
macro_rules! class {
($name:ident) => ({
#[inline(always)]
fn get_class(name: &str) -> Option<&'static $crate::runtime::Class> {
unsafe {
static CLASS: ::std::sync::atomic::AtomicPtr<$crate::runtime::Class> =
::std::sync::atomic::AtomicPtr::new(0 as *mut _);
// `Relaxed` should be fine since `objc_getClass` is thread-safe.
let ptr = CLASS.load(::std::sync::atomic::Ordering::Relaxed);
if ptr.is_null() {
let cls = $crate::runtime::objc_getClass(name.as_ptr() as *const _);
CLASS.store(cls as *mut _, ::std::sync::atomic::Ordering::Relaxed);
if cls.is_null() { None } else { Some(&*cls) }
} else {
Some(&*ptr)
}
}
}
match get_class(concat!(stringify!($name), '\0')) {
Some(cls) => cls,
None => panic!("Class with name {} could not be found", stringify!($name)),
}
})
}

#[doc(hidden)]
#[macro_export]
macro_rules! sel_impl {
Expand Down
2 changes: 1 addition & 1 deletion src/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub unsafe trait Message {
# use objc::Message;
# fn main() {
let obj: &Object;
# obj = unsafe { msg_send![Class::get("NSObject").unwrap(), new] };
# obj = unsafe { msg_send![class!(NSObject), new] };
let sel = sel!(isKindOfClass:);
// Verify isKindOfClass: takes one Class and returns a BOOL
let result = obj.verify_message::<(&Class,), BOOL>(sel);
Expand Down
2 changes: 2 additions & 0 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,8 @@ mod tests {
assert!(cls.instance_size() > 0);
assert!(cls.superclass().is_none());

assert!(Class::get(cls.name()) == Some(cls));

let metaclass = cls.metaclass();
// The metaclass of a root class is a subclass of the root class
assert!(metaclass.superclass().unwrap() == cls);
Expand Down
4 changes: 2 additions & 2 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ pub fn custom_class() -> &'static Class {
decl.register();
});

Class::get("CustomObject").unwrap()
class!(CustomObject)
}

pub fn custom_protocol() -> &'static Protocol {
Expand Down Expand Up @@ -179,7 +179,7 @@ pub fn custom_subclass() -> &'static Class {
decl.register();
});

Class::get("CustomSubclassObject").unwrap()
class!(CustomSubclassObject)
}

pub fn custom_subclass_object() -> CustomObject {
Expand Down

0 comments on commit 6584f53

Please sign in to comment.