45

I am writing an Objective-C class but it uses an API written in C. This is mostly fine as mixing C calls with Objective-C calls causes few problems.

However one of the API call requires a call back method (example):

success = CFHostSetClient(host, MyCFHostClientCallBack, &context); 

Where MyCFHostClientCallBack is a C function defined like this:

static void MyCFHostClientCallBack(CFHostRef host, CFHostInfoType typeInfo, const CFStreamError *error, void *info); 
  1. Can/How do I call an Objective-C method in place of this?
  2. Can/Should I mix C functions with my Objective-C calls?
  3. How do I mix C functions with Objective-C methods?

5 Answers 5

54

Mixing C and Objective-C methods and function is possible, here is a simple example that uses the SQLite API within an iPhone App: (course site)

Download the Zip file (09_MySQLiteTableView.zip)

C functions need to be declared outside of the @implementation in an Objective-C (.m) file.

int MyCFunction(int num, void *data) { //code here... } @implementation - (void)MyObjectiveCMethod:(int)number withData:(NSData *)data { //code here } @end 

Because the C function is outside of the @implementation it cannot call methods like

[self doSomething] 

and has no access to ivars.

This can be worked around as long as the call-back function takes a userInfo or context type parameter, normally of type void*. This can be used to send any Objective-C object to the C function.

As in the sample code, this can be manipulated with normal Objective-C operations.

In addition please read this answer: Mixing C functions in an Objective-C class

Sign up to request clarification or add additional context in comments.

8 Comments

C functions do not need to be outside @implementation.
@Bavarious: The compiler lets you get away with functions inside @implementation, but it is still not part of the @implementation, as functions cannot belong to a class. As a matter of style, I leave it outside of @implementation, and recommend others do the same, to make that clear.
Actually, C functions inside @implementation blocks have the unique property of being able to access private and protected ivars directly. Thus, from my own experience, it's become a strong idiom to place C functions that "belong" to a class inside the corresponding implementation. I'd recommend updating your answer.
To clarify, they will have access to private ivars if they are given access to an instance of the class. E.g. int MyCFunction(MyClass* self, int num, void *data). This is very similar to what's going on behind the scenes when your -MyObjectiveCMethod:withData: objc-method is compiled.
c functions can actually also be defined inside the implementation scope and no one would complain. And, they can't access self in any case inside or outside the @implementation, simply because aren't object's methods.
|
28

To call Objective-C code from a C callback I would use something like:

void * refToSelf; int cCallback() { [refToSelf someMethod:someArg]; } @implementation SomeClass - (id) init { self = [super init]; refToSelf = self; } - (void) someMethod:(int) someArg { } 

5 Comments

Just one addition: many callbacks allow to specify "user data"; in this case, the object pointer could be used as the user data (and you don't need to global variable refToSelf anymore). It might be that your callback function supports "user data" in the info pointer.
I'm receiving runtime memory leaked warnings with ARC enabled using above code. stackoverflow.com/questions/11840943/…
This is buggy implementation. It ASSUMES you only have one instance of ComeClass. void *refToSelf; is a static/global variable. Every time you [[SomeClass alloc] init] your refToSelf will be overwritten by the last instance created. The cCallback will NOT call the right SomeClass, but rather, always the last one created. Furthermore - it will crash when the last created SomeClass is released (at least in the code above) because you don't clear the refToSelf upon dealloc of the SomeClass.
To add on to what @ashcatch said: if your callback is given a void *userData, make sure to cast it to a pointer to your Objective-C class instance with (__bridge id)(userData) to avoid warnings and leaks.
If some class is Singleton this is ok. but Its not singleton, agree with @MottiShneor answer.
8

Can/How do I call an Objective-C method in place of this?

You cannot.

Can/Should I mix C function in with my Objective-C call?

Yes. Write a C function and use that as the callback to the CF function.

How do I mix C function with Objective-C methods?

You can set self as the info pointer in your context structure. That will be passed to the callback. Then, in the callback, cast the info pointer back to id:

MyClass *self = (id)info; 

You can then send self messages. You still can't directly access instance variables, though, since a C function is outside of the @implementation section. You'll have to make them properties. You can do this with a class extension. (Contrary to what that document says, you would not declare the extension inside @implementation, but in the same file with it, generally right above it.)

Comments

5

What I've always found helpful in this situation is to make an Obj-C wrapper on top of the C API. Implement what you need to using C functions, and build an Objective-C class (or two) on top of it, so that's all the outside world will see. For example, in the case of a callback like this, you might make a C function that calls Obj-C delegate methods on other objects.

Comments

0

.m call function inside .c:

  • CrifanLib.h
#ifndef CrifanLib_h #define CrifanLib_h #include <stdio.h> void fileModeToStr(mode_t mode, char * modeStrBuf); #endif /* CrifanLib_h */ 
  • CrifanLib.c
#include "CrifanLib.h" #include <stdbool.h> void fileModeToStr(mode_t mode, char * modeStrBuf) { // buf must have at least 10 bytes const char chars[] = "rwxrwxrwx"; for (size_t i = 0; i < 9; i++) { // buf[i] = (mode & (1 << (8-i))) ? chars[i] : '-'; bool hasSetCurBit = mode & (1 << (8-i)); modeStrBuf[i] = hasSetCurBit ? chars[i] : '-'; } modeStrBuf[9] = '\0'; } 

called by Objective-C's .m:

#include “CrifanLib.h" @interface JailbreakDetectionViewController () @end @implementation JailbreakDetectionViewController … char* statToStr(struct stat* statInfo){ char stModeStr[10]; fileModeToStr(statInfo->st_mode, stModeStr); ... } ... 

done.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.