What you want is possible, but it's not method swizzling, since you don't want to switch to methods but add a new one. It can be done, thanks to Objective-C's dynamic nature, but it's still a dirty hack so also file a feature request with the library vendor.
What you want is class_addMethod()
and a C function with the actual implementation for that. One more thing, Objective-C methods are C methods, but with two implicit parameters, self
and _cmd
, which have to keep in mind (both when creating your C method and when telling class_addMethod
your methods signature. And here is an SSCE of how to pull something like that off:
#import <Foundation/Foundation.h>
#import <objc/runtime.h> // Required for class_addMethod()
@interface MyClass : NSObject
@end
@implementation MyClass
@end
@protocol MyProtocol <NSObject>
- (void)printString:(NSString *)string;
@end
// Note the method signature containing the
// two implicit parameters self and _cmd!
void MyClassPrinStringIMP(id self, SEL _cmd, NSString *string)
{
NSLog(@"Hi I'm %@:%s and this is the string: %@", self, sel_getName(_cmd), string);
}
void PimpMyClass()
{
// The last argument is the signature. First character is the return type, in our case void
// Then comes self and _cmd, followed by the NSString. You can use @encode() to find out how your
// type is encoded. Best is to build this string at runtime, since the encoding can change with architectures
class_addMethod([MyClass class], @selector(printString:), (IMP)MyClassPrinStringIMP, "v@:@");
}
int main(int argc, const char * argv[])
{
@autoreleasepool
{
PimpMyClass();
id foo = [[MyClass alloc] init]; // id, to silence the compiler!
[foo printString:@"Hello World"];
}
return 0;
}
Example output:
Hi I'm <MyClass: 0x100101810>:printString: and this is the string: Hello World
Edit: Something that you may find is that the passed object is checked at runtime wether it conforms to a protocol or not using conformsToProtocol:
. Since this code just adds the method implementation, it would still fail, but you can tell the runtime that you totally do implement that protocol with this one function call:
class_addProtocol([MyClass class], @protocol(MyProtocol));
Alternative: proxies
Objective-Cs dynamism and message forwarding is already praised by @JasperBlues, however, there is one particular class in Objective-C that is designed to do just that: NSProxy
. It is designed to intercept sent messages and dispatching them dynamically to the relevant target, and does use the high-level NSInvocation
approach. If you can pass a proxied object in some way as the delegate (depending on what your code allows for and what not), creating a NSProxy
subclass might be the cleanest way to go.
However, note though that you then end up with a shim object that wraps over your other object, which comes with its own bag of pain and will break when you try to directly access variables via ->
syntax. It's not a perfectly invisible proxy, but good enough for most cases.