ForwardInvocation does exist, but it is slow and has the annoying problem of compiler warnings. So that got me to thinking -- is there a way to use macroes to quickly implement a bunch of getter methods that get the property in question from another object?
For example, if I have a Car object, it might want to implement the following:
Car.h:
@class SparkPlug;
@class Engine;
. . .
-(int) nPistons;
-(float) horsepower;
-(SparkPlug*) sparkPlug;
Car.m:
. . .
-(int) nPistons {
return self.engine.nPistons;
}
-(float) horsepower {
return self.engine.horsepower;
}
-(SparkPlug*) sparkPlug {
return self.engine.sparkPlug;
}
Question -- would it be possible to set up some macroes so that by making one change somewhere, I could add another such method to both the header and implementation files?
e.g. MagicForwardingMacro (nPistons, int, engine);
Ideally, in such a way that the macroes would be reusable if I later wanted to later use a similar strategy to get the firstName, lastName, placeOfBirth, and dateOfBirth properties of a Person from his or her birthCertificate.
The easiest way is probably to add the methods dynamically:
Elaborating on the second step:
For each type, add a method like
-(int)getEngineInt {
return (int()(id,SEL))(objc_msgSend)(engine, _cmd);
}
Note that for structs you need objc_msgSend_stret and for floats/doubles you might need objc_msgSend_fpret (I think you only need it on i386; not sure about AMD64). The easy hack to support both the simulator and device is something like (I forget the macro name GCC uses...)
#if __i386
#define objc_msgSend_fpret objc_msgSend
#endif
Now to implement +resolveInstanceMethod:
, you need to know the class you're forwarding to ahead of time. Let's say it's Engine.
+(BOOL)instancesRespondToSelector:(SEL)name
{
return [Engine instancesRespondToSelector:name];
}
+(BOOL)resolveInstanceMethod:(SEL)name
{
// Do we want to super-call first or last? Who knows...
if ([super resolveInstanceMethod:name]) { return YES; }
// Find the return type, which determines the "template" IMP we call.
const char * returntype = [Engine instanceMethodSignatureForSelector:name].methodReturnType;
if (!returnType) { return NO; }
// Get the selector corresponding to the "template" by comparing return types...
SEL template = NULL;
if (0 == strcmp(returntype,@encode(int))
{
sel = @selector(getEngineInt);
}
else if (0 == strcmp(Returntype,@encode(float))
{
...
}
if (!sel) { return NO; }
Method m = class_getInstanceMethod(self,template);
return class_addMethod(self, name, method_getImplementation(m), method_getTypeEncoding(m));
}
Alternatively, there's a slightly undocumented method -forwardingTargetForSelector: which may be fast enough for your needs.
EDIT: Alternatively, you can loop over the properties/methods dynamically. There doesn't appear to be an obvious way to introspect categories, but you can define them in a protocol, do something like @interface Engine:NSObject<Engine> ... @interface Car(DynamicEngine)<Engine>
and use objc_getProtocol("Engine")
and then protocol_copyMethodDescriptionList()/protocol_copyPropertyList() to get the methods, and then add the getters. I'm not sure if properties are added to the "method description list". Also note that the "copy" functions do not copy methods/properties from superclasses, which (in this case) is what you want.