How to call an Objective-C Method from a C Method?
Asked Answered
C

6

45

I have an Obj-C object with a bunch of methods inside of it. Sometimes a method needs to call another method inside the same object. I can't seem to figure out how to get a C method to call a Obj-C method...

WORKS: Obj-C method calling an Obj-C method:

[self objCMethod];

WORKS: Obj-C method calling a C method:

cMethod();

DOESN'T WORK: C method calling an Obj-C method:

[self objCMethod];     // <--- this does not work

The last example causes the compiler spits out this error:

error: 'self' undeclared (first use in this function)

Two questions. Why can't the C function see the "self" variable even though it's inside of the "self" object, and how do I call it without causing the error? Much thanks for any help! :)

Computerize answered 14/8, 2009 at 20:28 Comment(1)
For anyone stumbling over this in 2022: There is no such thing as a "C method". Methods are attached to objects. C does not know about objects, it is a procedural language, not object-oriented. C only has functions, which are not attached to objects, but "float free". As such, there is no object to which self could refer.Lowson
B
55

In order for that to work, you should define the C method like this:

void cMethod(id param);

and when you call it, call it like this:

cMethod(self);

then, you would be able to write:

[param objcMethod];

In your cMethod.

This is because the self variable is a special parameter passed to Objective-C methods automatically. Since C methods don't enjoy this privilege, if you want to use self you have to send it yourself.

See more in the Method Implementation section of the programming guide.

Bootery answered 14/8, 2009 at 20:44 Comment(3)
Shouldn't "[self objcMethod];" be "[param objcMethod];"Yseulte
When I use this solution, I find none of my methods in the objective-c class can be called with parameters.Detent
This generates a warning about incompatible pointer types when creating the thread: void* ThreadMethod(id param) { [param setupAVPlayer]; return NULL; } . . . // INCOMPATIBLE POINTER TYPES WARNING OCCURS AT CREATION int threadError = pthread_create(&posixThreadID, &attr, &ThreadMethod, NULL);Diplex
E
28

I know your question is already answered by Aviad but just to add to the info since this is not unrelated:

In my case I needed to call an Objective-C method from a C function that I did not call myself (a Carbon Event function triggered by registering a global hotkey event) so passing self as a parameter was impossible. In this particular case you can do this:

Define a class variable in your implementation:

id thisClass;

Then in your init method, set it to self:

thisClass = self;

You can then call Objective-C methods from any C function in the class without the need to pass self as a parameter to the function:

void cMethod([some parameters]) {
    [thisClass thisIsAnObjCMethod];
}
Edmundedmunda answered 6/10, 2009 at 14:45 Comment(4)
Thanks! :) I actually ran into the same situation and did just that. In the @interface file, I put "id aSelf;" before @interface, and then set "aSelf = self;" in init (or awakeFromNib) for @implementation.Computerize
How do you even define id from a C function, given that it only has access to standard lib? Can I import any specific Apple library that will allow me to use 'id' in my C file?Detent
This does not work; it returns a 'duplicate symbol' error at runtime.Diplex
where to put id thisClass ?Urus
M
10

C function is not "inside of the self object". In fact, nothing is.

Objective-C methods effectively get self as an implicit argument, with magic done under the hood. For plain C functions, they aren't associated with any class or object, and there's no call magic, so no self. If you need it, you need to pass it to your C function explicitly as an argument.

Metamathematics answered 14/8, 2009 at 20:37 Comment(0)
P
7

To be totally truthful, there is no such thing as a C method. C has functions. To illustrate the difference, look at the following examples:

This is a working C program that defines a type and two functions that go along with it:

#include <stdio.h>

typedef struct foo_t {
    int age;
    char *name;
} Foo;

void multiply_age_by_factor(int factor, Foo *f) {
    f->age = f->age * factor;
}

void print_foo_description(Foo f) {
    printf("age: %i, name: %s\n", f.age, f.name);
}

int main() {
    Foo jon;
    jon.age = 17;
    jon.name = "Jon Sterling";

    print_foo_description(jon);
    multiply_age_by_factor(2, &jon);
    print_foo_description(jon);

    return 0;
}

Here is an Objective-C implementation of that program:

#import <Foundation/Foundation.h>

@interface Foo : NSObject {
    NSUInteger age;
    NSString *name;
}

@property (nonatomic, readwrite) NSUInteger age;
@property (nonatomic, copy) NSString *name;

- (void)multiplyAgeByFactor:(NSUInteger)factor;
- (NSString *)description;
- (void)logDescription;

@end


@implementation Foo 
@synthesize age;
@synthesize name;

- (void)multiplyAgeByFactor:(NSUInteger)factor {
    [self setAge:([self age] * factor)];
}

- (NSString *)description {
    return [NSString stringWithFormat:@"age: %i, name: %@\n", [self age], [self name]];
}

- (void)logDescription {
    NSLog(@"%@",[self description]);
}

@end


int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Foo *jon = [[[Foo alloc] init] autorelease];
    [jon setAge:17];
    [jon setName:@"Jon Sterling"];

    [jon logDescription];
    [jon multiplyAgeByFactor:2];
    [jon logDescription];

    [pool drain];

    return 0;
}

The output of the pure C program was:

age: 17, name: Jon Sterling
age: 34, name: Jon Sterling

The output of the Objective-C program was:

2009-08-25 17:40:52.818 test[8963:613] age: 17, name: Jon Sterling
2009-08-25 17:40:52.828 test[8963:613] age: 34, name: Jon Sterling

The only difference is all the junk that NSLog puts before the text. The functionality is exactly the same. So, in C, you can use something sort of like methods, but they are really just functions that include a pointer to a struct.

I don't think this answered your original question, but it did clear up some terminology issues you appear to have been having.

Pascale answered 26/8, 2009 at 0:42 Comment(2)
I appreciate the comment. Very helpful. Thanks! :)Computerize
No problem! Glad to have helped…Pascale
R
2

Another option to the answers given thus far is to use the objc_msgSend() function provided by the Objective-C runtime.

Rochellrochella answered 6/10, 2009 at 14:50 Comment(2)
Generally not a great idea. The calling conventions are a little arcane. On Apple's runtime, there are different versions of objc_msgSend for different return types, for example. Safer to let the compiler figure this out.Pursuer
Could you provide an example, advantages or disadvantages, please? Since it's you, Dave, I'll assume this is one of the better ways to go...Nev
S
0

There are two options from what I can tell. You should pick one depending on your needs.

Easy

The first one is encapsulating and exposing the ObjC API via C ABI compatible functions. This way, you will be able to call the function from C code and retain most type safety. Potentially, you could pass a reference to the ObjC class as argument struct objc_class *foo, but I have not tested this.

// foobar.m

int foo() {
  int bar = [NSFoo doSomething];
  return bar;
}

Flexible

The other option is to use the ObjC runtime. This way, you won't need to directly compile Objective C as part of your program. This approach comes with the drawback of completely loosing type safety. Furthermore, you will need to make decisions the compiler normally does for you. As an example, you will need to use objc_msgSend or objc_msgSend_stret depending on the context. Do not forget to include -framework Cocoa or whatever you need as part of your compiler flags.

// cocoa_example.c

#include <objc/runtime.h>

void *app_init(void) {
  // struct objc_class *foo and Class foo are equivalent!
  Class app_ptr = objc_getClass("NSApplication");
  if(app_ptr == NULL) {
    return NULL;
  }

  // NSApplication is only available as part of the
  // Objective C code, which is not included.
  // Therefore the type gets lost.
  void *app = objc_msgSend(app_ptr, "sharedApplication");

  return app;
}

As far as I can tell, it is really nice for language bindings, because you only have to care about compiling C code and passing types around becomes super easy.

Shrewd answered 29/4 at 12:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.