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

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! 👓 ⌘ 🐻

Related

How to declare a method that takes a block as a callback

I have a method that I'd like to call like so:
void (^someblock)()=^{
NSLog(#"I want to know");
};
[Item getCacheAndCallback:self.menuItemID andCallback:someblock];
How would I declare this in my header file? I have tried
+(void)getCacheAndCallback:(int)menuItemID andCallback:(^());
but says "Expected a type". I thought this was supposed to go to void for type.
+(void)getCacheAndCallback:(int)menuItemID andCallback:(void(^)(void))completionBlock;
Putting block types directly into method signatures can get messy as you've found. I would suggest you use a typedef to make it cleaner:
typedef void(^VoidBlock)();
Now you can write your block declaration as:
VoidBlock someBlock = ^{ NSLog(#"I want to know"); };
and your method declaration as:
+ (void) getCacheAndCallback:(int)menuItemID andCallback:(VoidBlock)callBack;
Note: that typedef does not introduce a new type, just a shorthand. This means, for example, that you can declare a block using the full type (or even a different typedef with the same full type) and use it as VoidBlock.
Block syntax is weird. I keep this link handy to reference any time I happen to forget something: fuckingblocksyntax.com

getting return value type of an instance method in runtime

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

Function pointer problem in Objective-C

So I am trying to store a series of methods in an array (if that made sense).
void *pointer[3];
pointer[0] = &[self rotate];
pointer[1] = &[self move];
pointer[2] = &[self attack];
//...
What I am trying to do is have an array of stuff and based on the type of the object in the array, a certain method is invoked. And instead of having a bunch of if statement saying something like:
if ([[myArray objectAtIndex:0] type] == robot]) {
//Do what robots do...
}
else if (...) {
}
else {
}
And having this in a timer I was hoping to make it something like this:
pointer[[[myArray objectAtIndex:0] type]]; //This should invoke the appropriate method stored in the pointer.
Right now the code above says (the very first block of code):
Lvalue required as unary '&' operand.
If you need any clarification just ask.
Also, just to let you know all the method I am calling are type void and don't have any parameters.
You can't just make a function pointer out of an Objective-C function using the & Operator.
You'll want to look into:
#selector
NSInvocation
Blocks
Any of these can do what you want. Definitely read about selectors (the #selector compiler directive and the SEL type) if you're unfamiliar with that (it's a basic concept that you'll need a lot). Blocks are fairly new (available since Mac OS X 10.6 and iOS 4) and they'll save you a ton of work where you would have needed target/selector, NSInvocation or callback functions on earlier versions of Mac OS X and iOS.
Use function pointers if you need to pass around references to C functions but when working with methods on Objective-C objects you should really use selectors and the SEL type.
Your code would then be something like:
SEL selectors[3];
selectors[0] = #selector(rotate);
selectors[1] = #selector(move);
selectors[2] = #selector(attack);
...
[self performSelector:selectors[n]];

How do I write to an NSObject from within a C function that doesn't see Obj-C variables?

I'm trying to get some code going that lets me display raw trackpad data from my macbook pro, like the app FingerMgmt. Unfortunately, no one seems to have the source for FingerMgmt. I did find some other source code that kind of works, however. I was able to NSLog the data I wanted to see like this:
int callback(int device, Finger *data, int nFingers, double timestamp, int frame) {
for (int i=0; i<nFingers; i++) {
Finger *f = &data[i];
NSLog(#"Frame %7d: Angle %6.2f, ellipse %6.3f x%6.3f; "
"position (%6.3f,%6.3f) vel (%6.3f,%6.3f) "
"ID %d, state %d [%d %d?] size %6.3f, %6.3f?\n",
f->frame,
f->angle * 90 / atan2(1,0),
f->majorAxis,
f->minorAxis,
f->normalized.pos.x,
f->normalized.pos.y,
f->normalized.vel.x,
f->normalized.vel.y,
f->identifier, f->state, f->foo3, f->foo4,
f->size, f->unk2);
//todo-get data from raw C to obj-C variable
}
return 0;
}
But whenever I try to store any of the data to an Obj-c string or variable, the C code does not see the variable as having been declared. Because of this, I cannot write to any text fields or graphical displays in Obj-C, and I cannot store the data to a variable that Obj-c can access.
Basically, I need a way to write to an Obj-C variable or object from within the callback.
On a side note, I had a very similar problem with an iPhone app a while back, and I ended up fixing it by somehow declaring the app delegate within the C code and writing to or reading from the variable like this-
me.delegate=(id <UIApplicationDelegate,UITabBarControllerDelegate>)[[UIApplication sharedApplication] delegate];//allows access to the delegate within C function
me.delegate.number0=5;//writes to this variable in the delegate
For some reason, I can not seem to adapt this to my current situation. I always get the error that "me" is undeclared.
A Objective-C method can access instance variables because it is automagically passed a hidden parameter with the public name self - any reference to an instance variable, say fred, is translated by the compiler into a field reference, say self->fred (and a similar translation for property references).
For your C function callback to access the fields of any object (or call an object's methods) you need to pass the function a reference to the object. Two simple ways:
Add an argument to the function. Many C callback protocols include a general "user defined" values which is passed around as void *, if you are calling one of these pass your object reference as this value and cast it within the C function back to the correct Objective-C type.
Pass the object via a global (or file static) variable, e.g. static NSSomeType *objectForCallback;. This method works when you're stuck with an existing C callback protocol which doesn't support a user defined value. However it is not thread or re-entrant safe as you are sharing a single static variable.
In both cases make sure the objected is retain'ed if you're not using garbage collection.
In response to comment
Case 1: You will see C functions declared which (a) take a callback function and (b) a user-defined value to pass to that function on every call. For example:
typedef T ...;
T findMatching(T *buffer, // an array of T to search
size_t count, // number of items in array
int (*matcher)(T item, void *user), // user function for match, 1st arg is item, 2nd user-supplied value
void *userValue); // user-supplied value to pass to matcher
If you are faced with C function like this you can pass a (retain'ed if needed) Objective-C object as userValue and cast it back to its Objective-C type inside matcher. For example:
int myMatcher(T item, void *user)
{
NSMutableDictionary *myDictionary = (NSMutableDictionary *)user;
...
}
- (void) someMethod
{
NSMutableDictionary *sharedWithC = ...;
...
T found = findMatching(buffer, count, myMatcher, (void *)sharedWithC);
...
}
Case 2: Objective-C is (a superset of) C. You declare a global just as you would in C. For example (little checking, not thread safe):
static NSMutableDictionary *myGlobalDictionary = nil; // "static" makes the variable only visible to code in the same file
- (void) setupTheSharedDictionary
{
myGlobalDictionary = [[[NSMutableDictionary alloc] init] retain];
}
- (void) releaseTheSharedDictionary
{
if(myGlobalDictionary != nil)
{
[myGlobalDictionary release];
myGlobalDictionary = nil;
}
}
In response to second comment
I'm guessing you are trying to use some third party (Google?) code. That code defines a callback protocol - a C function type. You cannot just redefine that C function type adding an extra argument and expect the third party code to magically cope!
So unless you intend to change the C you can use the second approach - store the reference to Objective-C object in a global. In your case this will be something like:
static MT2AppDelegate *sharedWithCAppDelegateReference;
int callback(...)
{
...
[sharedWithCAppDelegateReference->L1 setStringValue:#"Hellofff"];
...
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
sharedWithCAppDelegateReference = self; // store so C can pick it up
...
MTRegisterContactFrameCallback(dev, callback);
...
}
But remember this is not thread or re-entrant safe - you are effectively passing a function parameter via a global variable. If you need it to be thread/re-entrant safe you need to get a bit more involved.

Functions in Objective-C

I am trying to write a function which returns a string created from two input strings;
but when I try the function declaration
NSString Do_Something(NSString str1, NSString str2)
{
}
the compiler gets sick. (Worked fine for a different function with int arguments.)
If I change the input arguments to pointers to strings, in also gets sick.
So how do I pass Objective-C objects into a function?
All Objective-C objects being passed to functions must be pointers. Rewriting it like this will fix your compiler error:
NSString *Do_Something(NSString *str1, NSString *str2) { }
Also, please keep in mind that this is a (C-style) function and not an instance method written on an Objective-C object. If you wanted this to actually be a method on an object it would probably look something like this:
NSString *doSomethingWithString1:(NSString *)str1 string2:(NSString *)str2 { }
I say "probably" because you can name it however you want.
Functions are perfectly fine in Objective-C (and in fact earn some of the language's benefits).
See my answer to C function always returns zero to Objective C, where someone was trying what you are and had a problem with the compiler assuming return type. The structure that I set up there is important when you are using functions, just like when you are using objects and methods. Be sure to get your headers right.
To be pedantic, you're using a function definition of:
NSString *DoSomething(NSString *str1, NSString *str2) {
// Drop the _ in the name for style reasons
}
And you should be declaring it in a .h file like so:
NSString *DoSomething(NSString *str1, NSString *str2);
Just like C.
that doesn't work for me. i've just declared in the .h:
NSString *myFunction(NSDecimal *value);
and i type in the .m:
NSString *myFunction(NSDecimal *value){
//code
}
but always i get an error saying expected '(' before '*' token
now is fixed. for some reason... sorry.