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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
use super::{sys, Method, Property, Sel, BOOL};
use crate::core::{Arc, ObjectType};
use std::{
cell::UnsafeCell,
cmp,
ffi::CStr,
fmt, hash, mem,
os::raw::{c_char, c_int},
panic::RefUnwindSafe,
ptr,
};
#[cfg(feature = "malloced")]
use malloced::Malloced;
/// An Objective-C class.
///
/// See [documentation](https://developer.apple.com/documentation/objectivec/class).
///
/// # Usage
///
/// This is an opaque type meant to be used behind a shared reference `&Class`,
/// which is semantically equivalent to `Class _Nonnull`.
///
/// A nullable class is defined as `Option<&Class>`, which is semantically
/// equivalent to `Class _Nullable`.
#[repr(C)]
pub struct Class {
// Stores data that may be mutated behind a shared reference. Internal
// mutability triggers undefined behavior without `UnsafeCell`.
_data: UnsafeCell<[u8; 0]>,
}
// This type is used globally, so we must be able to share it across threads.
unsafe impl Sync for Class {}
unsafe impl Send for Class {}
// Although this uses `UnsafeCell`, it does not point to any Rust types.
impl RefUnwindSafe for Class {}
impl fmt::Debug for Class {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Class").field(&self.name()).finish()
}
}
impl PartialEq for Class {
#[inline]
fn eq(&self, other: &Self) -> bool {
ptr::eq(self, other)
}
}
impl Eq for Class {}
impl PartialOrd for Class {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Class {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
(self as *const Self).cmp(&(other as *const Self))
}
}
impl hash::Hash for Class {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
(self as *const Self).hash(state);
}
}
impl Class {
/// Returns the class definition of a specified class, or `None` if the
/// class is not registered with the Objective-C runtime.
#[inline]
#[doc(alias = "objc_getClass")]
pub fn get(name: &CStr) -> Option<&'static Class> {
unsafe { objc_getClass(name.as_ptr()) }
}
/// Returns the number of classes registered with the Objective-C runtime.
#[inline]
pub fn count() -> usize {
unsafe { objc_getClassList(ptr::null_mut(), 0) as usize }
}
/// Returns all classes registered with the Objective-C runtime.
#[doc(alias = "objc_getClassList")]
pub fn all() -> Vec<&'static Class> {
let len = Self::count();
let mut all = Vec::<&'static Class>::with_capacity(len);
unsafe {
objc_getClassList(all.as_mut_ptr(), len as c_int);
all.set_len(len);
}
all
}
#[inline]
#[allow(unused)] // Used by `foundation`
pub(crate) unsafe fn alloc<T: ObjectType>(&self) -> Arc<T> {
// TODO: Add `cfg` use `objc_msgSend` on older platforms where this
// symbol does not exist, such as macOS 10.10+.
//
// This may require reading the `-mmacosx-version-min` flag somehow.
extern "C" {
fn objc_alloc();
}
let objc_alloc: unsafe extern "C" fn() = objc_alloc;
let objc_alloc: unsafe extern "C" fn(&Class) -> Arc<T> = mem::transmute(objc_alloc);
objc_alloc(self)
}
/// Calls `[[self alloc] init]`.
#[inline]
pub(crate) unsafe fn alloc_init<T: ObjectType>(&self) -> Arc<T> {
// TODO: Add `cfg` use `objc_msgSend` on older platforms where this
// symbol does not exist.
//
// This may require reading the `-mmacosx-version-min` flag somehow.
extern "C" {
fn objc_alloc_init();
}
let objc_alloc_init: unsafe extern "C" fn() = objc_alloc_init;
let objc_alloc_init: unsafe extern "C" fn(&Class) -> Arc<T> =
mem::transmute(objc_alloc_init);
objc_alloc_init(self)
}
/// Returns `true` if this class implements or inherits a method that can
/// respond to a specified message.
///
/// See [documentation](https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418583-respondstoselector).
#[inline]
#[doc(alias = "respondsToSelector")]
pub fn responds_to_selector(&self, selector: Sel) -> bool {
unsafe { _msg_send_any_cached![self, respondsToSelector: selector => BOOL] }.into()
}
/// Returns `true` if instances of this class implement or inherit a method
/// that can respond to a specified message.
///
/// See [documentation](https://developer.apple.com/documentation/objectivec/nsobject/1418555-instancesrespondtoselector).
#[inline]
#[doc(alias = "instancesRespondToSelector")]
pub fn instances_respond_to_selector(&self, selector: Sel) -> bool {
unsafe { _msg_send_any_cached![self, instancesRespondToSelector: selector => BOOL] }.into()
}
/// Returns the name of this class.
#[inline]
#[doc(alias = "class_getName")]
pub fn name(&self) -> &CStr {
unsafe { CStr::from_ptr(class_getName(self)) }
}
/// Returns this class's superclass, or `None` if this is a root class
/// (e.g. [`NSObject`](struct.NSObject.html)).
#[inline]
#[doc(alias = "class_getSuperclass")]
pub fn superclass(&self) -> Option<&Class> {
unsafe { class_getSuperclass(self) }
}
/// Returns an iterator over the superclasses of this class.
#[inline]
pub fn superclass_iter(&self) -> impl Iterator<Item = &Class> + Copy {
#[derive(Copy, Clone)]
struct Iter<'a>(&'a Class);
impl<'a> Iterator for Iter<'a> {
type Item = &'a Class;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let superclass = self.0.superclass()?;
self.0 = superclass;
Some(superclass)
}
}
// There are no more superclasses after the root is reached.
impl std::iter::FusedIterator for Iter<'_> {}
Iter(self)
}
/// Returns the number of superclasses of this class.
#[inline]
pub fn superclass_count(&self) -> usize {
self.superclass_iter().count()
}
/// Returns `true` if this class has a superclass.
#[inline]
pub fn is_subclass(&self) -> bool {
self.superclass().is_some()
}
/// Returns `true` if this class is a subclass of, or identical to, the
/// other class.
pub fn is_subclass_of(&self, other: &Self) -> bool {
if self == other {
true
} else {
self.superclass_iter().any(|superclass| superclass == other)
}
}
/// Returns the size of instances of this class.
#[inline]
pub fn instance_size(&self) -> usize {
unsafe { class_getInstanceSize(self) }
}
/// Returns a reference to the data for a class method defined by `name`, or
/// `None` if this class or its superclasses do not implement a class method
/// with the specified selector.
///
/// Note that this function searches superclasses for implementations,
/// whereas [`copy_class_method_list`](Self::copy_class_method_list) does
/// not.
///
/// See [documentation](https://developer.apple.com/documentation/objectivec/1418887-class_getclassmethod?language=objc).
#[inline]
#[doc(alias = "class_getClassMethod")]
pub fn get_class_method(&self, name: Sel) -> Option<&Method> {
unsafe { sys::class_getClassMethod(self, name).as_ref() }
}
/// Returns the class methods implemented by this class, or `None` if this
/// class implements no instance methods.
///
/// To get the implementations of instance methods that may be implemented
/// by superclasses, use [`get_class_method`](Self::get_class_method).
///
/// This calls
/// [`class_copyMethodList`](https://developer.apple.com/documentation/objectivec/1418490-class_copymethodlist?language=objc)
/// on the metaclass of this class.
#[cfg(feature = "malloced")]
#[inline]
#[doc(alias = "class_copyMethodList")]
pub fn copy_class_method_list(&self) -> Option<Malloced<[&Method]>> {
use std::{mem::MaybeUninit, os::raw::c_uint};
// TODO: Move this function into `objc::sys` module.
extern "C" {
fn object_getClass(obj: *const Class) -> *const Class;
}
let superclass = unsafe { object_getClass(self) };
let mut len = MaybeUninit::<c_uint>::uninit();
unsafe {
let data = sys::class_copyMethodList(superclass, len.as_mut_ptr());
if data.is_null() {
None
} else {
Some(Malloced::slice_from_raw_parts(
data.cast::<&Method>(),
len.assume_init() as usize,
))
}
}
}
/// Returns a reference to the data for an instance method defined by
/// `name`, or `None` if this class or its superclasses do not implement an
/// instance method with the specified selector.
///
/// Note that this function searches superclasses for implementations,
/// whereas [`copy_instance_method_list`](Self::copy_instance_method_list)
/// does not.
///
/// See [documentation](https://developer.apple.com/documentation/objectivec/1418887-class_getclassmethod?language=objc).
#[inline]
#[doc(alias = "class_getInstanceMethod")]
pub fn get_instance_method(&self, name: Sel) -> Option<&Method> {
unsafe { sys::class_getInstanceMethod(self, name).as_ref() }
}
/// Returns the instance methods implemented by this class, or `None` if
/// this class implements no instance methods.
///
/// To get the implementations of instance methods that may be implemented
/// by superclasses, use [`get_instance_method`](Self::get_instance_method).
///
/// See [`documentation`](https://developer.apple.com/documentation/objectivec/1418490-class_copymethodlist?language=objc).
#[cfg(feature = "malloced")]
#[inline]
#[doc(alias = "class_copyMethodList")]
pub fn copy_instance_method_list(&self) -> Option<Malloced<[&Method]>> {
use std::{mem::MaybeUninit, os::raw::c_uint};
let mut len = MaybeUninit::<c_uint>::uninit();
unsafe {
let data = sys::class_copyMethodList(self, len.as_mut_ptr());
if data.is_null() {
None
} else {
Some(Malloced::slice_from_raw_parts(
data.cast::<&Method>(),
len.assume_init() as usize,
))
}
}
}
/// Returns a property of `self` with `name`.
///
/// See [documentation](https://developer.apple.com/documentation/objectivec/1418597-class_getproperty).
#[inline]
#[doc(alias = "class_getProperty")]
pub fn get_property<'a>(&'a self, name: &CStr) -> Option<&'a Property> {
extern "C" {
fn class_getProperty(class: &Class, name: *const c_char) -> Option<&Property>;
}
unsafe { class_getProperty(self, name.as_ptr()) }
}
/// Returns a `malloc`-ed list of properties declared by `self`.
///
/// Any properties declared by superclasses are not included.
///
/// See [documentation](https://developer.apple.com/documentation/objectivec/1418553-class_copypropertylist).
#[cfg(feature = "malloced")]
#[inline]
#[doc(alias = "class_copyPropertyList")]
pub fn copy_property_list(&self) -> Option<Malloced<[&Property]>> {
use std::{mem::MaybeUninit, os::raw::c_uint};
extern "C" {
fn class_copyPropertyList<'a>(
class: &'a Class,
out_count: *mut c_uint,
) -> *mut &'a Property;
}
let mut len = MaybeUninit::<c_uint>::uninit();
unsafe {
let data = class_copyPropertyList(self, len.as_mut_ptr());
if data.is_null() {
None
} else {
Some(Malloced::slice_from_raw_parts(
data,
len.assume_init() as usize,
))
}
}
}
}
extern "C" {
fn objc_getClass(name: *const c_char) -> Option<&'static Class>;
fn objc_getClassList(buf: *mut &'static Class, buf_len: c_int) -> c_int;
fn class_getName(class: &Class) -> *const c_char;
fn class_getSuperclass(class: &Class) -> Option<&Class>;
fn class_getInstanceSize(class: &Class) -> usize;
}