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]];
Related
I have a property, that in Objective-C I created like this:
self.myProperty = [[MyClass alloc] initWithCompletionBlock:^(MyClass *object) {
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomethingAfterInitialization];
});
}];
And it worked great. Initialization of the MyClass object could create an indeterminate amount of time, so I passed a completionHandler in to it. When it finished, doSomethingAfterInitalization: would handle business.
Now in Swift, I'm trying to create the same object and assign it to a property, with problems.
The property never will change, so it makes sense to me to create it as a Swift constant.
So I'm trying it like this:
let myProperty = MyClass(completionBlock:{ (MyClass) -> (Void) in dispatch_async(dispatch_get_main_queue(), doSomethingAfterInitialization())})
To me that seemed like a direct translation... but the Swift compiler tells me that's not correct, via the error
Use of instance member 'doSomethingAfterInitialization' on type 'MyViewController'; did you mean to use a value of type 'MyViewController' instead?
Well that didn't help much. So instead I tried changing the call to the doSomethingAfterInitialization function to self. doSomethingAfterInitialization(), in which case I see
Value of type '(NSObject) -> () -> TodayWidgetTableViewController' has no member 'doSomethingAfterInitialization'
Any idea how I can fix this? Obviously my initializer is a little weird in the first place, so I'm wondering if this is something that doesn't really translate at all to Swift.
I have a callback function in C which looks like this:
void cfunc(char* labelUpdateText)
This callback function is supposed to update the text of a label in my app's UI by changing the label's current text to whatever labelUpdateText is.
So in cfunc I'm trying to call this Objective C function:
-(void)updateLabel:(char*) text
{
[_labelProp setStringValue:[NSString stringWithFormat:#"%#", [NSString stringWithUTF8String:text]]];
}
However, I can't call the objective C function from my C function.
I tried like this:
updateLabel:labelUpdateText
But when I build the code, I get the warning 'Expression result unused'
And the 'updateLabel:labelUpdateText' doesn't execute.
What am I doing wrong?
PS: I am new to Objective C!
You need to provide the callback function with a pointer to the view containing the label and some means of updating it. All callback functions should have a context pointer of some sort which can hold information allowing them to perform context-specific actions. If you cannot change the callback function semantics then you are stuck I'm afraid, however if you can, do it something like this:
void cfunc(const char *labelUpdateText, void *contextInfo) {
YourView *view = (YourView *)contextInfo;
[view updateLabelWithText:[NSString stringWithUTF8String:labelUpdateText]];
}
You will need to provide the updateLabelWithText method in YourView and if you are using ARC there will be some bridging to do as well during the casting of contextInfo (the compiler will help you there though).
Why not just make a wrapper C function in the Objective C class that calls the ObjC syntax and call the C function from your C program that needs to make the callback.
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! 👓 ⌘ 🐻
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.
I'm kind of new with objective c and I'm trying to pass an argument by reference but is behaving like it were a value. Do you know why this doesn't work?
This is the function:
- (void) checkRedColorText:(UILabel *)labelToChange {
NSComparisonResult startLaterThanEnd = [startDate compare:endDate];
if (startLaterThanEnd == NSOrderedDescending){
labelToChange.textColor = [UIColor redColor];
}
else{
labelToChange.textColor = [UIColor blackColor];
}
}
And this is the call:
UILabel *startHourLabel; // This is properly initialized in other part of the code
[self checkRedColorText:startHourLabel];
Thanks for your help
Objective-C only support passing parameters by value. The problem here has probably been fixed already (Since this question is more than a year old) but I need to clarify some things regarding arguments and Objective-C.
Objective-C is a strict superset of C which means that everything C does, Obj-C does it too.
By having a quick look at Wikipedia, you can see that Function parameters are always passed by value
Objective-C is no different. What's happening here is that whenever we are passing an object to a function (In this case a UILabel *), we pass the value contained at the pointer's address.
Whatever you do, it will always be the value of what you are passing. If you want to pass the value of the reference you would have to pass it a **object (Like often seen when passing NSError).
This is the same thing with scalars, they are passed by value, hence you can modify the value of the variable you received in your method and that won't change the value of the original variable that you passed to the function.
Here's an example to ease the understanding:
- (void)parentFunction {
int i = 0;
[self modifyValueOfPassedArgument:i];
//i == 0 still!
}
- (void)modifyValueOfPassedArgument:(NSInteger)j {
//j == 0! but j is a copied variable. It is _NOT_ i
j = 23;
//j now == 23, but this hasn't changed the value of i.
}
If you wanted to be able to modify i, you would have to pass the value of the reference by doing the following:
- (void)parentFunction {
int i = 0; //Stack allocated. Kept it that way for sake of simplicity
[self modifyValueOfPassedReference:&i];
//i == 23!
}
- (void)modifyValueOfPassedReference:(NSInteger *)j {
//j == 0, and this points to i! We can modify i from here.
*j = 23;
//j now == 23, and i also == 23!
}
Objective-C, like Java, only has pass-by-value. Like Java, objects are always accessed through pointers. "objects" are never values directly, hence you never assign or pass an object. You are passing an object pointer by value. But that does not seem to be the issue -- you are trying to modify the object pointed to by the pointer, which is perfectly allowed and has nothing to do with pass-by-value vs. pass-by-reference. I don't see any problem with your code.
In objective-c, there is no way to pass objects by value (unless you explicitly copy it, but that's another story). Poke around your code -- are you sure checkRedColorText: is called? What about [startDate compare:endDate], does it ever not equal NSOrderedDescending? Is labelToChange nil?
Did you edit out code between this line
UILabel *startHourLabel;
and this line?
[self checkRedColorText:startHourLabel];
If not, the problem is that you're re-declaring your startHourLabel variable, so you're losing any sort of initialization that was there previously. You should be getting a compiler error here.
Here are the possibilities for why this doesn't work:
the label you pass in to checkRedColorText is not the one you think it is.
the comparison result is always coming out the same way.
... actually, there is no 3.
You claim you initialised startHourLabel elsewhere, but, if it is a label from a nib file, you should not be initialising it at all. It should be declared as an IBOutlet and connected to the label in the nib with interface builder.
If it is not a label in the nib i.e. you are deliberately creating it programmatically, you need to check the address of the label you initialise and check the address of the label passed in to checkRedColorText. Either NSLog its address at initialisation and in checkRedColorText or inspect it with the debugger.