I've generated a list of methods and properties of a class using the ObjC runtime, so that those can be called later from a bridge using NSInvocation.
The problem is that for those methods that the runtime can't generate a signature I'm getting an error.
For instance calling the property getter for direction
in an instance of SKFieldNode
throw the exception NSInvalidArgumentException
, I’m guessing that’s because vector_float3
has no encoded type, its type is '' (i.e. no character type)
This is how to test what I'm describing:
Method method = class_getInstanceMethod([SKFieldNode class], @selector(direction));
const char *types = method_getTypeEncoding(method);
NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:types];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
SKFieldNode *field = [SKFieldNode node];
field.direction = (vector_float3){1,2,3};
[inv setTarget:field];
[inv setSelector:@selector(direction)]; // (*)
[inv invoke];
vector_float3 v;
[inv getReturnValue:&v];
NSLog(@"%f %f %f", v.x, v.y, v.z);
(*) "NSInvalidArgumentException", "-[NSInvocation setArgument:atIndex:]: index (1) out of bounds [-1, 0]"
How can I tell, using introspection, whether a method can be safely called in that way?
I tried testing the number of arguments that NSMethodSignature
returns, but the value is wrong for a method with missing encoded types, for instance this two methods will return 2, counting the target and selector, so that the remaining arguments are not taken into account.
- setDirection:(vector_float3)d1 direction:(vector_float3)d2;
- setDirection:(vector_float3)d;
I’ve also noticed that the direction property is not available in Swift
That make me think it’s because of this very same reason. So I wouldn't mind to drop support for those methods either in the custom bridge.
This is a fairly simple check to make sure you don't have any improperly encoded arguments:
BOOL isMethodSignatureValidForSelector(NSMethodSignature *signature, SEL selector) {
// This could break if you use a unicode selector name, so please don't do that :)
const char *c_str = sel_getName(selector);
unsigned numberOfArgs = 2;
while (*c_str) {
if (*c_str == ':') {
numberOfArgs++;
};
c_str++;
}
return ([signature numberOfArguments] == numberOfArgs);
}