// Add an instance variable decl.add_ivar::<u32>("_number");
// Add an ObjC method for getting the number externfnmy_number_get(this:&Object,_cmd:Sel)->u32{ unsafe{*this.get_ivar("_number")} } unsafe{ decl.add_method(sel!(number), my_number_getasexternfn(&Object,Sel)->u32); }
decl.register(); #} ```
*/
use std::ffi::CString; use std::mem; use std::ptr;
use runtime::{BOOL, Class, Imp, NO, Object, Protocol, Sel, self}; use {Encode, EncodeArguments, Encoding, Message};
/// Types that can be used as the implementation of an Objective-C method. pubtrait MethodImplementation { /// The callee type of the method. type Callee: Message; /// The return type of the method. type Ret: Encode; /// The argument types of the method. type Args: EncodeArguments;
/// Returns self as an `Imp` of a method. fn imp(self) -> Imp;
}
macro_rules! method_decl_impl {
(-$s:ident, $r:ident, $f:ty, $($t:ident),*) => ( impl<$s, $r $(, $t)*> MethodImplementation for $f where $s: Message, $r: Encode $(, $t: Encode)* { type Callee = $s; type Ret = $r; type Args = ($($t,)*);
fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString { letmut types = ret.as_str().to_owned(); // First two arguments are always self and the selector
types.push_str(<*mut Object>::encode().as_str());
types.push_str(Sel::encode().as_str());
types.extend(args.iter().map(|e| e.as_str()));
CString::new(types).unwrap()
}
fn log2_align_of<T>() -> u8 { let align = mem::align_of::<T>(); // Alignments are required to be powers of 2
debug_assert!(align.count_ones() == 1); // log2 of a power of 2 is the number of trailing zeros
align.trailing_zeros() as u8
}
/// A type for declaring a new class and adding new methods and ivars to it /// before registering it. pubstruct ClassDecl {
cls: *mut Class,
}
impl ClassDecl { fn with_superclass(name: &str, superclass: Option<&Class>)
-> Option<ClassDecl> { let name = CString::new(name).unwrap(); let super_ptr = superclass.map_or(ptr::null(), |c| c); let cls = unsafe {
runtime::objc_allocateClassPair(super_ptr, name.as_ptr(), 0)
}; if cls.is_null() {
None
} else {
Some(ClassDecl { cls: cls })
}
}
/// Constructs a `ClassDecl` with the given name and superclass. /// Returns `None` if the class couldn't be allocated. pubfn new(name: &str, superclass: &Class) -> Option<ClassDecl> {
ClassDecl::with_superclass(name, Some(superclass))
}
/// Adds a method with the given name and implementation to self. /// Panics if the method wasn't sucessfully added /// or if the selector and function take different numbers of arguments. /// Unsafe because the caller must ensure that the types match those that /// are expected when the method is invoked from Objective-C. pubunsafefn add_method<F>(&mutself, sel: Sel, func: F) where F: MethodImplementation<Callee=Object> { let encs = F::Args::encodings(); let encs = encs.as_ref(); let sel_args = count_args(sel);
assert!(sel_args == encs.len(), "Selector accepts {} arguments, but function accepts {}",
sel_args, encs.len(),
);
let types = method_type_encoding(&F::Ret::encode(), encs); let success = runtime::class_addMethod(self.cls, sel, func.imp(),
types.as_ptr());
assert!(success != NO, "Failed to add method {:?}", sel);
}
/// Adds a class method with the given name and implementation to self. /// Panics if the method wasn't sucessfully added /// or if the selector and function take different numbers of arguments. /// Unsafe because the caller must ensure that the types match those that /// are expected when the method is invoked from Objective-C. pubunsafefn add_class_method<F>(&mutself, sel: Sel, func: F) where F: MethodImplementation<Callee=Class> { let encs = F::Args::encodings(); let encs = encs.as_ref(); let sel_args = count_args(sel);
assert!(sel_args == encs.len(), "Selector accepts {} arguments, but function accepts {}",
sel_args, encs.len(),
);
let types = method_type_encoding(&F::Ret::encode(), encs); let metaclass = (*self.cls).metaclass() as *const _ as *mut _; let success = runtime::class_addMethod(metaclass, sel, func.imp(),
types.as_ptr());
assert!(success != NO, "Failed to add class method {:?}", sel);
}
/// Adds an ivar with type `T` and the provided name to self. /// Panics if the ivar wasn't successfully added. pubfn add_ivar<T>(&mutself, name: &str) where T: Encode { let c_name = CString::new(name).unwrap(); let encoding = CString::new(T::encode().as_str()).unwrap(); let size = mem::size_of::<T>(); let align = log2_align_of::<T>(); let success = unsafe {
runtime::class_addIvar(self.cls, c_name.as_ptr(), size, align,
encoding.as_ptr())
};
assert!(success != NO, "Failed to add ivar {}", name);
}
/// Adds a protocol to self. Panics if the protocol wasn't successfully /// added pubfn add_protocol(&mutself, proto: &Protocol) { let success = unsafe { runtime::class_addProtocol(self.cls, proto) };
assert!(success != NO, "Failed to add protocol {:?}", proto);
}
/// Registers self, consuming it and returning a reference to the /// newly registered `Class`. pubfn register(self) -> &'static Class { unsafe { let cls = self.cls;
runtime::objc_registerClassPair(cls); // Forget self otherwise the class will be disposed in drop
mem::forget(self);
&*cls
}
}
}
impl Drop for ClassDecl { fn drop(&mutself) { unsafe {
runtime::objc_disposeClassPair(self.cls);
}
}
}
/// A type for declaring a new protocol and adding new methods to it /// before registering it. pubstruct ProtocolDecl {
proto: *mut Protocol
}
impl ProtocolDecl { /// Constructs a `ProtocolDecl` with the given name. Returns `None` if the /// protocol couldn't be allocated. pubfn new(name: &str) -> Option<ProtocolDecl> { let c_name = CString::new(name).unwrap(); let proto = unsafe {
runtime::objc_allocateProtocol(c_name.as_ptr())
}; if proto.is_null() {
None
} else {
Some(ProtocolDecl { proto: proto })
}
}
fn add_method_description_common<Args, Ret>(&mutself, sel: Sel, is_required: bool,
is_instance_method: bool) where Args: EncodeArguments,
Ret: Encode { let encs = Args::encodings(); let encs = encs.as_ref(); let sel_args = count_args(sel);
assert!(sel_args == encs.len(), "Selector accepts {} arguments, but function accepts {}",
sel_args, encs.len(),
); let types = method_type_encoding(&Ret::encode(), encs); unsafe {
runtime::protocol_addMethodDescription( self.proto, sel, types.as_ptr(), is_required as BOOL, is_instance_method as BOOL);
}
}
/// Adds an instance method declaration with a given description to self. pubfn add_method_description<Args, Ret>(&mutself, sel: Sel, is_required: bool) where Args: EncodeArguments,
Ret: Encode { self.add_method_description_common::<Args, Ret>(sel, is_required, true)
}
/// Adds a class method declaration with a given description to self. pubfn add_class_method_description<Args, Ret>(&mutself, sel: Sel, is_required: bool) where Args: EncodeArguments,
Ret: Encode { self.add_method_description_common::<Args, Ret>(sel, is_required, false)
}
/// Adds a requirement on another protocol. pubfn add_protocol(&mutself, proto: &Protocol) { unsafe {
runtime::protocol_addProtocol(self.proto, proto);
}
}
/// Registers self, consuming it and returning a reference to the /// newly registered `Protocol`. pubfn register(self) -> &'static Protocol { unsafe {
runtime::objc_registerProtocol(self.proto);
&*self.proto
}
}
}
#[cfg(test)] mod tests { use test_utils;
#[test] fn test_custom_class() { // Registering the custom class is in test_utils let obj = test_utils::custom_object(); unsafe { let _: () = msg_send![obj, setFoo:13u32]; let result: u32 = msg_send![obj, foo];
assert!(result == 13);
}
}
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.