objc2
objc2 copied to clipboard
Add closure wrapper to allow more easily calling `action:target:`-like methods?
It is common in AppKit and UIKit to have methods that expect to run a selector on an object. These are usually legacy methods that were introduced before blocks were added to / common in Objective-C, or work this way because the event bubbles upwards.
Examples include NSMenuItem
, NSNotificationCenter
and UIGestureRecognizer
.
It would maybe be nice if we had some object wrapper around closures, such that one didn't have to go through the whole of declare_class!
just to handle an event?
I'm thinking something similar to:
// Implementation
unsafe trait Wrapable {
fn _get_sel() -> Sel;
}
impl Wrapable for Fn() -> R { ... }
impl Wrapable for Fn(T) -> R { ... }
impl Wrapable for Fn(T, U) -> R { ... }
// ...
fn wrap<F: 'static + Wrapable>(f: F) -> (Id<NSObject>, Sel) {
extern "C" fn method<F: Wrapable>(obj: &NSObject, ...args) -> R {
(IVAR.load(obj))(...args)
}
let sel = F::_get_sel();
let cls = { // Wrapped in a Once somehow?
// Unsure if TypeId is unique enough for different generics?
let builder = ClassBuilder::new(format!("WrappedClosure_{:?}", TypeId::of::<F>())).unwrap();
builder.add_ivar::<F>("closure");
builder.add_method(sel, method::<F>);
builder.register()
};
static IVAR = cls.instance_variable("closure").unwrap();
let obj: Id<NSObject> = msg_send_id![cls, new];
IVAR.load_ptr(&obj).write(f);
(obj, sel)
}
// Usage
let center = DistributedNotificationCenter::defaultCenter();
let (obj, sel) = wrap(|notification: &NSNotification| {
// handle notification
});
center.addObserver_selector_name_object(obj, sel, ns_string!("AppleInterfaceThemeChangedNotification"), None)