Last active
November 20, 2016 07:28
-
-
Save NSExceptional/4c8f49210a45c9782c2717109e3d2904 to your computer and use it in GitHub Desktop.
Use the Objc runtime and va_args to determine and use method parameters at runtime.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "TypeEncodings.h" | |
@interface Foo : NSObject | |
- (char)bar:(NSString *)bar baz:(int)b; | |
@end | |
@implementation Foo | |
- (char)bar:(NSString *)bar baz:(int)b { | |
printf("%s\n", bar.UTF8String); | |
return '#'; | |
} | |
@end | |
int main() { | |
// NSInvocation hack to invoke the desired IMP | |
void (*invokeWithIMP)(id, SEL, IMP) = (void *)[NSInvocation instanceMethodForSelector:NSSelectorFromString(@"invokeUsingIMP:")]; | |
@autoreleasepool { | |
Method method = Method(@selector(bar:baz:), [Foo class]); | |
IMP orig = originalIMP(method); | |
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:NSMethodSignatureOfMethod(method)]; | |
TypeEncoding returnType = invocation.methodSignature.methodReturnType[0]; | |
IMP imp = imp_implementationWithBlock(^void *(id receiver, ...) { | |
// Give invocation the original arguments | |
va_list args; | |
va_start(args, receiver); | |
for (NSInteger i = 2; i < invocation.methodSignature.numberOfArguments; i++) { | |
const char *typec = [invocation.methodSignature getArgumentTypeAtIndex:i]; | |
TypeEncoding type = typec[0]; | |
switch (type) { | |
case TypeEncodingInt: { | |
int baz = va_arg(args, int); | |
[invocation setArgument:&baz atIndex:i]; | |
printf("arg2: %d\n", baz); | |
break; | |
} | |
case TypeEncodingObjcObject: { | |
id bar = va_arg(args, id); | |
[invocation setArgument:&bar atIndex:i]; | |
printf("%s", [NSString stringWithFormat:@"arg1: %@\n", bar].UTF8String); | |
break; | |
} | |
default: { | |
printf("Some other type we don't care about: %c\n", (char)type); | |
exit(1); | |
} | |
} | |
} | |
// Invoke method with original IMP | |
invocation.target = receiver; | |
invokeWithIMP(invocation, 0, orig); | |
// We know the return length is greater than zero for this | |
// case, but if you wanted to dynamically return a value | |
// or not based on the return type, this is how to do it | |
if (invocation.methodSignature.methodReturnLength) { | |
void *ret = NULL; | |
[invocation getReturnValue:&ret]; | |
return ret; | |
} else { | |
return NULL; | |
} | |
}); | |
// Make Foo use our new method implementation | |
swizzle(method, imp); | |
Foo *f = [Foo new]; | |
char ret = [f bar:@"hello" baz:5]; | |
printf("Returned: %c\n", ret); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#import <objc/runtime.h> | |
#define Method(sel, cls) class_getInstanceMethod(cls, sel) | |
#define originalIMP(m) method_getImplementation(m) | |
#define NSMethodSignatureOfMethod(m) [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding(m)] | |
#define swizzle(m, imp) method_setImplementation(m, imp) | |
typedef NS_ENUM(NSUInteger, TypeEncoding) | |
{ | |
TypeEncodingUnknown = '?', | |
TypeEncodingChar = 'c', | |
TypeEncodingInt = 'i', | |
TypeEncodingShort = 's', | |
TypeEncodingLong = 'l', | |
TypeEncodingLongLong = 'q', | |
TypeEncodingUnsignedChar = 'C', | |
TypeEncodingUnsignedInt = 'I', | |
TypeEncodingUnsignedShort = 'S', | |
TypeEncodingUnsignedLong = 'L', | |
TypeEncodingUnsignedLongLong = 'Q', | |
TypeEncodingFloat = 'f', | |
TypeEncodingDouble = 'd', | |
TypeEncodingCBool = 'B', | |
TypeEncodingVoid = 'v', | |
TypeEncodingCString = '*', | |
TypeEncodingObjcObject = '@', | |
TypeEncodingObjcClass = '#', | |
TypeEncodingSelector = ':', | |
TypeEncodingArray = '[', | |
TypeEncodingStruct = '{', | |
TypeEncodingUnion = '(', | |
TypeEncodingBitField = 'b', | |
TypeEncodingPointer = '^' | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment