17

I have a C struct that contains a function pointer. Now, I have used this setup within C with no problems, but now I'm using this C struct in Objective-C and I need to pass a function (or selector) pointer that is defined in the Objective-C class.

1. Here is what I have for the Objective-C selector that needs to be passed as a pointer to the C function:

- (void)myObjCSelector:(int*)myIntArray
{
    // Do whatever I need with myIntArray
}

2. And here is where I run into a wall, Within Objective-C I'm trying to pass the selector as a pointer to the C function call: In place of "myObjCSelectorPointer" I need the proper syntax to pass the selector as a function pointer in this C function call:

passObjCSelectorPointerToCContext(cContextReference, myObjCSelectorPointer);

I did investigate this issue, but could mainly find several different ways of doing similar things, but I couldn't find anything specific for calling C functions and passing an Objective-C selector pointer.

Claudia
  • 726
  • 1
  • 6
  • 20
  • 2
    This is a great question because it highlights a common misconception about selectors being function pointers which is false and is held by most new objc programmers. – twerdster Aug 13 '11 at 16:26
  • I really do appreciate all the answers I'm getting, and I understand how important it is to learn and understand the differences between the selectors and functions. But for posterity and sanity of other users looking for the same issue, I was looking for a concise answer that directly relates to the question. I have read through the answers and I have gone through the links, but unless I'm missing something, I'm still not able to find the steps to reproduce and resolve the issue. – Claudia Aug 13 '11 at 16:54
  • I have updated my response with a second edit and a more "concise" answer. I would like to reiterate: this is not a good idea. – twerdster Aug 13 '11 at 22:17
  • @twerdster Thanks for your edit! I read it and it does make more sense now. I will test this tomorrow. As a note though. This C function that uses the pointer is in a library which is not at my disposal for editing. And it is not a library I can rewrite in Objective-C. So I'm basically stuck with it. And it definitely makes me nervous hearing the experts saying it is not a good idea. In any case, thank you very much for your help. – Claudia Aug 13 '11 at 23:35
  • if this is an external library then could you add the types of each of those variables and functions in your post as an update. It will help in understanding what needs to be done exactly. – twerdster Aug 14 '11 at 09:41
  • @twerdster Not sure what you mean. The cContextReference is a pointer to a C struct. myObjCSelectorPointer is what I was wishing for, but based on your answers can't get. The myObjCSelector would have been the function that was targeted by myObjCSelectorPointer. myIntArray is a parameter of that selector. Not sure what other kind of type details you would need. I don't think the actual library names of these are important, the behavior should be exactly the same. – Claudia Aug 15 '11 at 14:38
  • Sorry. I was indeed unclear. What I was looking for is the type of each of the arguments to passObjCSelectorPointerToCContext. So what is cContextReferences type? is it a pointer to a special struct? If so what are the components of the struct? – twerdster Aug 15 '11 at 15:25

3 Answers3

19

In objc a selector is not a function pointer. A selector is a unique integer that is mapped to a string in a method lookup table stored by the objc runtime. In the above case your method name would be myObjCSelector: and to get the unique selector for it you would type @selector(myObjCSelector:). However this would be of no use to you because it doesnt represent a particular implementation of a function.

What youre looking for is IMP. Refer to this SO question.

EDIT 2:

 IMP myObjCSelectorPointer = (void (*)(id,SEL,int*))[self methodForSelector:@selector(myObjCSelector:)];

Then you can call the method using

myObjCSelectorPointer(self,@selector(myObjCSelector:),myIntArray);

However, what this means you will need to make sure that you add the pointer to self in the c function call passObjCSelectorPointerToCContext. So it should look like this

passObjCSelectorPointerToCContext(cContextReference, self, myObjCSelectorPointer); 

when called from within the object that contains the method.

It is important to note though that using IMP is almost never the right technique. You should try to stick with pure Obj-C. Obj-C is quite efficient after the first call to a message because it uses temporal caching.

EDIT 1:

It's useful to understand why objc works in this way. The Apple documents explain it in depth. However a short explanation is as follows:

When you send a message to an object such as [myobject somemethod] the compiler won't immediately know which particular implementation of somemethod to call because there might be multiple classes with multiple overriden versions of somemethod. All of those methods have the same selector, irrespective of its arguments and return values and hence the decision about which implementation of somemethod is deffered to when the program is running. [myobject somemethod] gets converted by the compiler into a C function call:

objc_msgSend(myobject, @selector(somemethod))

This is a special function that searches each myobject class layout to see whether that class knows how to respond to a somemethod message. If not it then searches that class's parent and so on until the root. If none of the classes can respond to somemethod then NSObject defines a private method called forward where all unknown messages are sent.

Assuming that a class can respond to the somemethod message then it will also have a particular pointer of type IMP that points to the actual implementation of the method. At that point the method will be called.

There is considerably more to this procedure than I have described but the outline should be enough to help you understand what the goal of a selector is.

One final point is that the reason method names are mapped to unique integers via the @selector directive is so that the runtime doesn't have to waste time doing string comparisons.

Community
  • 1
  • 1
twerdster
  • 4,977
  • 3
  • 40
  • 70
  • +1 because you're absolutely correct, but -1 because I don't think using the raw `IMP` directly is the best way to go about doing this. And calling `objc_msgSend` directly is almost never a good idea. – Dave DeLong Aug 13 '11 at 16:04
  • So they cancelled out ;) I agree that using the direct IMP is almost never the best method. Ive updated accordingly. – twerdster Aug 13 '11 at 16:18
1

Basically, the answer is: Objective-C selectors are different from function pointers. You need two pieces of data to perform a selector. That is an object and the selector itself. You will need some glue to accomplish your task.

Check this question.

Community
  • 1
  • 1
Anton
  • 2,342
  • 15
  • 17
1

Do you have to use a function pointer? In Objective-C, you can get the function pointer to an arbitrary method implementation (known as an IMP), but this is extremely uncommon, and usually not a good idea. Calling objc_msgSend() directly is also not the greatest idea, because there are several different variants of objc_msgSend(), and the compiler automatically chooses different ones to use based on the return type of the method. Methods that return an object go through objc_msgSend(), but objects that return structs might go through objc_msgSend() or they might go through objc_msgSend_stret(). And if the method returns a double, then it goes through objc_msgSend_fpret()...

Documentation: Objective-C Runtime Reference: Sending Messages

Instead, I might recommend using a target-action pair, or using a block. Then you might do something like:

myContextRef->target = anObjcObject;
myContextRef->action = @selector(invokeMe:);

And when you're done, do:

[myContextRef->target performSelector:myContextRef->action withObject:someReturnInformation];

Or maybe use a block:

myContextRef->completionHandler = [^(id returnInformation) {
  [anObjcObject invokeMe:returnInformation];
} copy];

And then when you're done, do:

myContextRef->completionHandler(someReturnInformation);

(and don't forget to -release the block when you free the context)

Dave DeLong
  • 242,470
  • 58
  • 448
  • 498