Search code examples
objective-cmetaprogramminglate-binding

Dynamically bind method to selector at runtime


I want to programmatically associate code with selectors. I am not clear on how to do that in Objective C. In Ruby, I might override method_missing. In Common Lisp, I might define a macro. In Objective C, I can get part of the way there with @dynamic properties, but I'm unclear on how to actually implement them.

Here's a concrete example: I want to use an NSMutableDictionary to persistently store parts of my object. My class has two methods that handle the basic functionality, and a bunch of dynamic properties (matching @propertys exist in @interface):

@dynamic name;
@dynamic age;
@dynamic favoriteColor;
- (id)accessor:(NSString*)name {
    return [[self dict] objectForKey:name];
}
- (void)mutator:(NSString*)name value:(id)value{
    [[self dict] setObject:value forKey:name];
    [[self dict] writeToFile:[self filename] atomically:YES];
}

Now I am looking for a way to translate a call like

[myInstance setName:@"iter"];

into

[self mutator:@"name" value@"iter"];

I wonder if there is an idiomatic way to do that in ObjC.


Solution

  • This isn't really an idiomatic thing to do in Objective-C, and there's certainly nothing like a Lisp macro available. NSObject and the runtime do, however, provide three possible points for you to intercept and handle messages referring to methods that don't otherwise exist. In the order they are used by the runtime: resolveInstanceMethod:, forwardInvocation: and doesNotRespondToSelector:. The documentation for each of them explains their use and gives some examples.

    The first requires you to actually write out and add a method to the class, which doesn't seem like it will achieve the dynamic state of affairs you desire. The last by default raises an exception and doesn't provide for any return value. Almost certainly, forwardInvocation is what you want to look into. It allows your object to ask another object to handle a method call, including the passed arguments; it should be possible for you to make your object handle the call itself in a way that at least gets you close to what you're going for.

    Also, the "Message Forwarding" chapter of the Runtime Programming Guide gives some examples of tasks similar to your requirement.