views:

59

answers:

4

Some of Apple's obj-c API's still use C functions, e.g.:

-(NSArray * ) sortedArrayUsingFunction: (NSInteger (*)(id, id, void *))comparator context:(void *)context

...which is great, except I'm struggling to see how you can store fn-pointers inside ObjC classes.

e.g. to share the same "sort" function in different parts of your program. Say you have different data, in different contexts/classes, but you want the same sort in both places (for consistency).

I'm sure this is simple, but either my C is too rusty, or there's some gotcha. I tried sticking a plain variable inside the header file:

NSInteger (*)(id, id, void *) myComparator;

...and all I get is a compiler error:

Expected identifier or '(' before ')' token

A: 

Function pointers are a little weird, because the name goes inside the type definition.

If you want to pass around a function pointer for a method like this:

- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;

You would write a function like this:

NSInteger myComparisonFunction(id left, id right, void *context) {
    // do stuff...
}

And a typedef like this:

typedef NSInteger (ComparisonFunc *)(id, id, void *);

Then in your class you can declare an instance variable like this:

ComparisonFunc compFunc;

And a property like this:

@property (nonatomic) ComparisonFunc compFunc;

Then to set the property you can call:

myObject.compFunc = myComparisonFunction;

And within myObject you can use it like this:

sortedArray = [array sortedArrayUsingFunction:compFunc context:NULL];
benzado
You missed the point of the question. This is nothing to do with "how do you use the method call", it's "how do you pass the function (your second quoted text) to another class, which then passes the function on later"
Adam
Oh, I see. Sorry, as I said, I wasn't sure I understood. I'll edit mine to make more sense, but Laurent Etiemble's answer is the one you're looking for.
benzado
+1  A: 

Is it really necessary to store the pointer? Why not just include the .h with the function declaration, then pass in the reference to the function?

Sam Dufel
Good question. I'm not 100% sure why, but the project I've been given already has the functions inside the classes, not the headers. Some of the functions have worryingly generic names, so it might be a problem of name clash somewhere.
Adam
FYI, if the functions are not declared static, they will all be part of the same namespace, and two functions with the same name will trigger a linker error.
benzado
On further investigation ... there are cases where we use different sort functions at runtime based on user behaviour. It's much easier to use fn-pointers to handle this than to jump through hoops - since Apple requires us to send them a fn-pointer *anyway* :).
Adam
+1  A: 

Instead of doing:

NSInteger (*)(id, id, void *) myComparator;

Use this instead:

NSInteger (* myComparator)(id, id, void *);

(This is just like block syntax, except blocks use ^ instead of *)

Dave DeLong
Thanks! That's the stupid mistake I was making :)
Adam
+1  A: 

You can define the function pointer as a type (with typedef) and then use it in your class definitions. For example

In a common header:

typedef NSInteger (*COMPARATOR)(id, id, void *);

In the first class:

@interface MyClass : NSObject {
    NSObject *anotherField;
    COMPARATOR thecomparator;
}

- (COMPARATOR)comparator;

- (void)setComparator:(COMPARATOR) cmp;

@end

And in the second class:

@interface MyOtherClass : NSObject {
    NSObject *afield;
    COMPARATOR thecomparator;
}

- (COMPARATOR)comparator;

- (void)setComparator:(COMPARATOR) cmp;

@end

The type COMPARATOR is then used as any other type.

Edit: I add some methods to show how to pass and retrieve function pointer.

Laurent Etiemble