44

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?
Alex Zavatone
  • 4,106
  • 36
  • 54
Richard Stelling
  • 25,607
  • 27
  • 108
  • 188

5 Answers5

52

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

Richard Stelling
  • 25,607
  • 27
  • 108
  • 188
  • 33
    C functions do not need to be outside `@implementation`. –  Apr 17 '11 at 12:11
  • 13
    @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. – Peter Hosey Dec 01 '11 at 23:32
  • 24
    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. – Josh Rosen Dec 02 '11 at 00:00
  • 4
    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. – Josh Rosen Dec 02 '11 at 00:13
  • 1
    @JoshRosen: Interesting, but I think that qualifies as an implementation detail, not a language feature explicitly defined and guaranteed to exist. – Peter Hosey Dec 02 '11 at 05:13
  • No, that's a supported language feature – Catfish_Man May 09 '13 at 01:56
  • pass self as an id parameter in a c function – amar Jun 11 '13 at 11:13
  • 2
    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. – Matteo Gobbi Jan 30 '19 at 22:20
27

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
{
}
diciu
  • 29,133
  • 4
  • 51
  • 68
  • 8
    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. – ashcatch Apr 29 '09 at 12:38
  • I'm receiving runtime memory leaked warnings with ARC enabled using above code. http://stackoverflow.com/questions/11840943/runtime-memory-leaked-warnings-when-executing-objective-c-code-within-c-code-wit – docchang Aug 07 '12 at 07:13
  • 2
    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. – Motti Shneor Dec 03 '14 at 10:22
  • 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. – VinceFior Jul 12 '15 at 08:45
  • If some class is Singleton this is ok. but Its not singleton, agree with @MottiShneor answer. – damithH Dec 07 '15 at 04:23
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.)

iamreptar
  • 1,461
  • 16
  • 29
Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
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.

Marc Charbonneau
  • 40,399
  • 3
  • 75
  • 82
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.

crifan
  • 12,947
  • 1
  • 71
  • 56