I sometimes see the question “How do I cast a method into a function pointer?” come up during classes or on some form of social media.   One problem programmers have, which asking questions, is phrasing the question too specifically.  In this case, the real question is “How do I use an Objective-C method as a callback for a C library?”  Casting a method to a function pointer is one possible way of solving the problem.  Is it the right way?

Nope.  Why not? Inside the Bracket (http://blog.bignerdranch.com/2833-inside-the-bracket-part-1-open-for-business/) pointed that that although Objective-C methods are actually C functions, they’re C functions that take two hidden arguments: self, and the selector, before they start consuming the actual set of arguments to the function. That’s what prevents you from using methods as C callbacks.

Say you’re using a library that does text manipulations, and it has a callback function that gives you a string and a piece of boolean state, and then you process the string and return it. You decide to make a function that converts the string into Swedish Chef speak:


NSString *encheferizeString (NSString *string, BOOL useChicken, void *info);


And you register it with the toolkit, like


SetModificationCallback (textManger, encheferizeString, infoPointer);

The library then goes off and does its work, eventually feeling the need to call your function. The library calls your function, passing along the string to modify, the boolean flag, and an info pointer, (also known as user data, the context, a reference constant, or simply a rock to hide some data under).  In memory, the arguments might look like this:





But!  you say. I have a method already that I want to use!


– (NSString *) encheferizeString: (NSString *) string

                      useChicken: (BOOL) useChicken;


Surely there’s some way, like getting the IMP from -methodForSelector: or something, and casting it to the callback type?  Yes, you *can* try that:


        IMP method = [cheferizer methodForSelector: @selector(encheferizeString:useChicken:)];

        SetModificationCallback (textManger, method, cheferizer);


and get a compiler warning (you are fixing your warnings (http://blog.bignerdranch.com/798-a-bit-on-warnings/), aren’t you?)


XXCheferizer.m:36:40: warning: incompatible pointer types 

    passing ‘IMP’ (aka ‘id (*)(__strong id, SEL, …)’) 

    to parameter of type ‘TextManagerCallback’ (aka ‘void (*)(NSString *__strong, BOOL)’)


        SetModificationCallback (NULL, method, NULL);


And now we’re back to “how do you cast a method into a function pointer?”

The compiler is happy to move bit patterns around, but it might take some coercing with casts (in this case, looks like a TextManagerCallback function pointer type) to quiet the warnings.  At runtime, though, things are not happy.  -encheferizeString:useChicken: is expecting its arguments in this order:




They don’t line up with what the C library will be passing.  Here they are side-by-side:



The C library will be passing the string, the boolean flag, and the info pointer.  The method, when it starts running, will pull in the value for self, but instead will get the string pointer. It’ll pull in the value for _cmd, the selector, but instead it’ll pull in the useChicken boolean.  The string will actually reference the info pointer, and the useChicken parameter will have some random value.  This is not good.


The Fix


How do you fix it?  Pass your object, the one with the method you want to run, as the info pointer.  Then use a little C function that casts the info pointer back into an object.  Once you have your object pointer back, you can use it with square brackets as you normally do:


XXCheferizer *cheferizer = [[XXCheferizer alloc] initWithDialect: kSwedish];

SetModificationCallback (textManger, encheferizeString, (__bridge void *)cheferizer);


NSString *encheferizeString (NSString *string, BOOL bawkbawk, void *info) {

    XXCheferizer *borkbork = (__bridge id) info;

    [borkbork encheferizeString: string  useChicken: bawkbawk];



For Reals, man


Hopefully, through the medium of muppets and poultry, I got the basic point across.  Time for an actual use-case.  Core Graphics has a datatype called CGShadingRef, used to fill in areas with custom shading.  It’s like CGGradient, but with more control.  To use CGShading, you construct a C function that gets called repeatedly while drawing happens.  Inside of that function, you check a parameter to see how far along the drawing is, and use that to decide which color to use.  A sample project, ShadyDeals, can be found at (https://github.com/markd2/ShadyDeals). All the fun happens in XXShadyView’s (https://github.com/markd2/ShadyDeals/blob/master/ShadyDeals/XXShadyView.m) implementation.  I won’t mention the work necessary to get a all the CGShadingRef moving pieces configured.  Well, OK I just mentioned it, but trust me it’s a bit of work to get it set up, which is just a distraction.

Here’s a shading function that will be fed, via the location parameter, a float value ranging from 0.0 to 1.0.  This function uses the progress value for all the color components:

static void shadingCallback (void *info, const float *location, float *results) {

    float thing = *location;


    results[0] = thing;  // Red

    results[1] = thing;  // Green

    results[2] = thing;  // Blue

    results[3] = 1.0;    // Alpha

} // shadingCallback


Resulting in a grayscale ramp:





Throw in some cyclical functions to generate some groovy colors:


static void shadingCallback (void *info, const float *location, float *results) {

    float thing = *location;


    results[0] = thing;

    results[1] = sin(M_PI * 2 * thing);

    results[2] = cos(M_PI * 2 * thing);

    results[3] = 1.0;


} // shadingCallback





Objects are great for storing bits of state that can affect behavior, as well has being a place to attach those behaviors to.  Wouldn’t it be nice to use a method here, for easy access to different attributes of the view?  Let’s do that.

A shader function is created with CGFunctionCreate, which takes your function pointer, your context pointer, and some bookkeeping:


    CGFunctionCallbacks callback = { 0, shadingCallback, NULL };

    CGFunctionRef shaderFunction = CGFunctionCreate ((__bridge void *)self,  // context/ info

                                                     1,     // number of inputs for domain


                                                     4,     // number of inputs for range



The interesting parts in bold.  The callback function is first put into a structure (which includes a version number, zero, and a callback for releasing the info pointer, NULL).  This structure is then used to create the shader function object.  shaderFunction encapsulates the callback, as well as the info pointer.  The info pointer is self, which is the UIView that’s drawing itself on the screen.  The C callbacks you saw earlier just ignore the info pointer.


Methodical Treatment


Here’s a method to do the calculations:


– (void) evalShadingAtLocation: (float) location

              returningResults: (float *) results {


    if (self.fancyColors) {

        results[0] = location;

        results[1] = sin(M_PI * 2 * location);

        results[2] = cos(M_PI * 2 * location);

        results[3] = 1.0;

    } else {

        results[0] = location;

        results[1] = location;

        results[2] = location;

        results[3] = 1.0;



} // evalShadingAtLocation


It’s just copy/paste of the C-callback code, but puts both the gray-ramp and the psychedelia in one place, using a property to decide which one to use.  Time to get it called. As a reminder, we can’t just cram it into the CGFunctionCallbacks structure because of the immiscibility of the function arguments:





What should shadingCallback look like now?


static void shadingCallback (void *info, const float *in, float *out) {

    XXShadyView *view = (__bridge XXShadyView *)info;


    [view evalShadingAtLocation: *in

          returningResults: out];


} // shadingCallback


Cast the info pointer to the proper type, and then message your view (via the info pointer) as usual.



But, but, Your Wrong!!1!


Sometimes after explaining the problem with C-function vs Objective-C method argument layout, I get someone who provides a Startling Counterexample that renders my argument moot and shames my family unto the seventh generation.  I try it, and sure enough, it seems to run correctly.  When that happens, it’s usually a C callback function with a signature like:


void callbackFunction (void *info, … additional arguments);


The counterexample implementation of the method looks like:


– (void) neenerNeener: (int) additionalArguments {

    // oh look, the arguments are not used in here.

    NSLog (@”ha ha”);



“It prints out HA HA and doesn’t crash!”  In a sense, this could actually “work.”  How?  Well, the info pointer, which would be pointing to an object, comes first in the list of arguments, like this:




But notice that the method doesn’t use any arguments.  There would be definitely be bugs if the method tried to do something with any arguments that were passed in.  When the method goes to grab its first parameter, it will actually be picking up the second parameter provided by the C library. If you’re lucky, the types will be completely incompatible (passing a float as an argument, which then gets treated as an NSString point) and crash spectacularly at runtime.  If you’re unlucky, you’ll just corrupt some unsuspecting chunk of data.



Wrap Up


What’s the ultimate take-away?  By being aware of how things work under the hood, specifically how methods are passed their arguments, you can understand why the compiler is giving you grief when you try to stuff an IMP into an arbitrary C function pointer, and how to work around it.





Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s