getting return value type of an instance method in runtime - objective-c

I want get the return value class of an instance in runtime. The thing it's that I have a SEL type var where I store a selector. I have a variable named id _instance that points to an instance that I know it performs the selector. Before perform the method I want to know if I have to do:
NSObject* returnValue=[_instance performSelector:_selector withObject:params.params];
or:
[_instance performSelector:_selector withObject:params.params];
I have read a post where someone explain the way to have that with objective-c runtime:
Method m = class_getClassMethod([_instance class], _selector);
char ret[256];
method_getReturnType(m, ret, 256);
NSLog(#"Return type: %s", ret);
But the outputs is nothing like ret is empty.
Really it can be enough to know if it's a void or have a return type but I don't know where to search. I have read the objective-c runtime reference but the only thing I found is the method_getReturnType.... Any idea?

If you're looking for an instance method, you need to use class_getInstanceMethod rather than class_getClassMethod. Class methods and instance methods are obviously different things.

After searching a while I found the library that uses Spotify for this kind of stuff, the name is MAObjcRuntime and you can found it here

Related

Store Objective-C classes in array and use them

Say I have two classes, BulbDevice and FanDevice, both are subclasses of Device and has a method signature like this:
+ (BOOL)isMyId:(NSInteger)someId;
If I wanted to create a class I could test it out:
if ([BulbDevice isMyId:someId]) {
Device *dev = [BulbDevice alloc] initWithId:someId];
}
But what I really want is to create a factory method inside a factory class, with minimum fuss when new device are added:
+ (Device)createDevice:(NSInteger)someId {
// say I have an array registered
NSArray *arr = #[[BulbDevice class], [FanDevice class]];
// Loop through it.
Device *device;
for (Class *c in arr) {
// The idea is kind of like this but I'm not sure how to make it work
if ([c isMyId]) {
device = [[c alloc] init];
}
}
}
The idea is that I only need to update arr in the factory method. So I think it is good to have something like this. But I am not sure how to make it work.
EDIT:
I took out the asterisk, but it won't work:
for (Class c in arr) {
// Now I want to access the isMyId which is a static method,
// but I how do I cast to that class? I mean not an object of the class, but to that class itself.
if ([(Device)c isMyId:]) {
}
}
But I still need a way to access that class method. Error says Used type 'Device' where arithmetic or pointer type is required, and even if it works, I want to access class method, not sending message to an object.
Or shall I store NSString in the array instead? But it is hard to find way to access the class method as well.
If I understand correctly what you are trying to achieve, then your approach seems to be correct.
There is only one thing that needs to be fixed:
for (Class c in arr)
c variable is not a pointer - the asterisk should be removed. Your code works.
The Class type is not an NSObject type, and although it is a bit special it is object-like or object-equivalent, so you are able to send it messages and store it in collections like you're doing.
You don't use the asterisk as #MaxPevsner says, because Class isn't used as a normal pointer-to-object. Think of Class as a special type like id which also doesn't get the * when you use it to reference an object.

Is it safe to store id into real class pointer before checking type

I have a lot of code that looks like this:
id myObjectRaw = getObject();
if(![myObjectRaw isKindOfClass:[MyClass class]]) return nil;
MyClass * myObject = myObjectRaw;
...
Here id getObject() can return several kinds of object. However the above code feels clunky to me. Is it safe to write this?
MyClass * myObject = getObject();
if(![myObject isKindOfClass:[MyClass class]]) return nil;
...
The compiler doesn't complain, but I'm not sure that I'm not treading on undefined behaviuor if getObject returns an object not related to MyClass.
(And no, I can't use a super class, or interface, since I dont actually have control over all the classes that get returned.)
You can do it. Nothing undefined. The only danger is that if the type is wrong and you forget to check the type, it may crash due to unrecognized selector exception.
In compiled code, id, MyClass * and NSString * have no difference, they just a pointer to a ObjC object.
Both versions will work. The first feels clunky, but there are problems with the second one as well: Putting something into a variable of a specific type implies knowledge of its type, and checking the class of something that seems to be known already looks redundant. If someone (it might be you) looks at that code next year, he may find the class check superfluous and remove it.
I've been in a similar situation, and I went with a helper method that gives a properly typed result or nil, i.e.
-(Rectangle)getRectangleObject {
id data = getObject();
if ([data isKindOfClass:[Rectangle class]]) return data;
return nil;
}
This simplifies code and communicates the intention clearly.
If you need several different type checks, you can go with several methods, or pass the class to this helper method.
As long as all types of returned objects conform to NSObject protocol (Classes that inherit from NSObject class do) it is safe to use isKindOfClass: method.
So make sure getObject() method only returns objective-c classes that inherit from NSObject
EDIT
While compiler is fine with it, as #Eiko mentions someone reading the code will probably think the isKindOfClass: check is unnecessary. It is better to use the former code to let the reader know that getObject() might also return other types of objects.
When you use id myObjectRaw you are NOT defining what kind of object myObjectRaw is, thus the compiler won't know if MyClass * myObject = getObject(); is a valid operation or not. THe compiler assumes you know what you are doing. If getObject() returns an object that is different than MyClass or it's not a subclass of it your app may crash. This is a runtime error.
If getObject() returns different objects, you should be expecting at least one object of the kind of objects that can be returned. If need to handle different objects, you can always use if-else-if instructions like:
id myObjectRaw = getObject();
if([myObjectRaw isKindOfClass:[MyClass1 class]])
{
MyClass1 objectClass1 = myObjectRaw;
}
else if([myObjectRaw isKindOfClass[MyClass2 class]])
{
MyClass2 objectClass2 = myObjectRaw;
}
However, if the object returned is a MyClass2 object, and this class is a subclass of MyClass1 the first condition will be true. Therefore, the object will be saved as a MyClass1 object. If that's the case you need to establish priorities and put them accordingly in the nested if-else-if statement.

Is there a way to reflectively call a function in Objective-C from a string?

Are there any Objective-C runtime functions that will allow me to get a function (or block) pointer from a string identifying the function? I need some way to dynamically find and invoke a function or static method based on some form of string identifying it.
Ideally this function should be able to exist in any dynamically loaded library.
Looking at Objective-C Runtime Reference, the best bet looks like class_getClassMethod, but there don't appear to be any function-related functions in this reference. Are there other raw C ways of getting a pointer to a function by name?
if you want to invoke some static objc method, you can make it as a class method of a class
#interface MyClas : NSObject
+ (int)doWork;
#end
and call the method by
[[MyClass class] performSelector:NSSelectorFromString(#"doWork")];
if you real want to work with C-style function pointer, you can check dlsym()
dlsym() returns the address of the code or data location specified by
the null-terminated character
string symbol. Which libraries and bundles are searched depends on the handle
parameter If dlsym() is called with the special handle RTLD_DEFAULT,
then all mach-o images in the process
(except those loaded with dlopen(xxx, RTLD_LOCAL)) are searched in the order they were loaded. This
can be a costly search and should be avoided.
so you can use it to find the function pointer base on asymbol name
not sure why you want to do this, sometimes use function table can do
typedef struct {
char *name,
void *fptr // function pointer
} FuncEntry;
FuncEntry table[] = {
{"method", method},
{"method2", method2},
}
// search the table and compare the name to locate function, you get the idea
If you know method signature you can create selector to it with NSSelectorFromString function, e.g.:
SEL selector = NSSelectorFromString(#"doWork");
[worker performSelector:selector];
You may be able to do what you want with libffi. But unless you are doing something like create your own scripting language or something like that where you need to do this sort of thing a lot. It is probable overkill
I've wondered the SAME thing.. and I guess, after having researched it a bit.. there is NOT a "standard C" way to do such a thing.. (gasp).. but to the rescue? Objective C blocks!
An anonymous function.. that can be OUTSIDE any #implementation, etc...
void doCFunction() { printf("You called me by Name!"); }
Then, in your objective-C method… you can somehow "get" the name, and "call" the function...
NSDictionary *functionDict = #{ #"aName" : ^{ doCFunction(); } };
NSString *theName = #"aName";
((void (^)()) functionDict[theName] )();
Result: You called me by Name!
Loves it! 👓 ⌘ 🐻

getArgument of NSInvocation of current method always returns null

I want to get the name of the arguments of the current function I am in so that I can prepare loading that object from the filesystem if it's not present on the current instance. (for instance if [foo dictTest] is not available I want to load it's prior saved plist version into exactly that ivar)
I want to find the file by providing the ivar name that I provided as an argument to the current function.
This is the function code:
-(NSDictionary*)getCachedDictionary:(NSDictionary*)dict{
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:_cmd]];
NSString * firstArgument = nil;
[invocation getArgument:&firstArgument atIndex:2];
NSLog(#"Trying to get the objects ivar %#",firstArgument);
// right now during testing return nil
return nil;
}
As soon as the code reaches the NSLog I am getting a null value from firstArgument.
Why is that? Could it be possible that I would have to wait for the complete invocation of that current method I am in or is it actually better to create a proxy function that implicitly calls my class method via an invocation that eats the ivar name provided by setArgument so that I can use that argument string like I want?
Thanks a lot in advance!
P.S.: In this particular example I do not want to use KVC to identify the ivar and return it.
You've misunderstood the NSInvocation API. +[NSInvocation invocationWithMethodSignature:] creates a new NSInvocation that is keyed to accept arguments of the types defined by the method signature. It does not return an NSInvocation that corresponds to the current method invocation. This is pretty easy to see why:
- (void)doBar:(id)bip {
NSLog(#"hi there!")
}
- (void)doFoo {
NSMethodSignature *sig = [self methodSignatureForSelector:#selector(doBar:)];
NSInvocation *i = [NSInvocation invocationWithMethodSignature:sig];
}
When you create the invocation in doFoo for the doBar: method, it's obvious to see that the arguments must be empty, because doBar: hasn't been executed, and thus there is no argument. Changing #selector(doBar:) to _cmd wouldn't magically change anything.
So the next question: is there a way to get an NSInvocation for the current method invocation? Not that I know of. NSInvocation is an extremely complicated class, and constructing one from the current method would be a nightmare.
I strongly suggest finding a different approach to do whatever it is you want to do.
Even though the question is old and answered, here is a link that provides an easy and very elegant way to create an invocation instance for any selector/method that is known at compile time:
http://www.cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

How to declare a class that conforms to a protocol as parameter type?

Is there a way to give, as a parameter, a class that conforms to a certain protocol?
What I tried at first, with a bit of hope, was this:
-(NSString *) getKeyForMyProtocolClass(Class<MyProtocol>)aClass
But that causes
[aClass superclass];
to give the warning "Instance method 'superclass' found instead of class method 'superclass'". I get the same sort of warning for conformsToProtocol:.
Since it gives no such warnings when the parameter is (Class)aClass, it seems Class< MyProtocol> is not actually of the Class type.
I should not be sending NSObject< MyProtocol>, since I need to determine the right key according to the class as well as its superclasses, and only create and add a new object if nothing is set to that key yet.
I could check with conformsToProtocol, but then I'd have to return a nil value which is just messy. I'd prefer to stop the issue at compile time.
So in short, is there a type declaration for a class that conforms to a protocol?
You can just typecast your class object to prevent the compiler warning. I was able to do the following:
- (void)tempMethod:(Class<NSObject>)klass {
id a = [(Class)klass superclass];
NSLog(#"%#", a);
}
Since you know the type of the object(Class object) you're passing this should work fine.