如何从 C 方法调用 Objective-C 方法?

问题描述 投票:0回答:6

我有一个 Obj-C 对象,里面有很多方法。有时一个方法需要调用同一对象内的另一个方法。我似乎不知道如何让 C 方法调用 Obj-C 方法...

工作: Obj-C 方法调用 Obj-C 方法:

[self objCMethod];

工作: Obj-C 方法调用 C 方法:

cMethod();

不起作用: C 方法调用 Obj-C 方法:

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

最后一个示例导致编译器吐出此错误:

错误:“self”未声明(在此函数中首次使用)

两个问题。为什么 C 函数看不到“self”变量,即使它位于“self”对象内部,以及如何调用它而不导致错误?非常感谢您的帮助! :)

objective-c self
6个回答
55
投票

为了使其工作,您应该像这样定义 C 方法:

void cMethod(id param);

当你调用它时,这样调用它:

cMethod(self);

然后,你就可以写:

[param objcMethod];

在你的

cMethod

这是因为

self
变量是自动传递给 Objective-C 方法的特殊参数。由于C方法不享受这个特权,如果你想使用
self
你必须自己发送。

请参阅编程指南的方法实现部分了解更多信息。


28
投票

我知道 Aviad 已经回答了你的问题,但只是添加到信息中,因为这并非无关紧要:

在我的例子中,我需要从我自己没有调用的 C 函数中调用 Objective-C 方法(通过注册全局热键事件触发的 Carbon Event 函数),因此将 self 作为参数传递是不可能的。在这种特殊情况下,您可以这样做:

在您的实现中定义一个类变量:

id thisClass;

然后在你的 init 方法中,将其设置为 self:

thisClass = self;

然后,您可以从类中的任何 C 函数调用 Objective-C 方法,而无需将

self
作为参数传递给函数:

void cMethod([some parameters]) {
    [thisClass thisIsAnObjCMethod];
}

10
投票

C 函数不是“在

self
对象内部”。事实上,什么都不是。

Objective-C 方法有效地将

self
作为隐式参数,在幕后完成了魔法。对于普通 C 函数,它们不与任何类或对象关联,并且没有调用魔法,因此没有
self
。如果您需要它,您需要将其作为参数显式传递给您的 C 函数。


7
投票

说实话,不存在 C 方法这样的东西。 C有函数。为了说明差异,请看以下示例:

这是一个有效的 C 程序,定义了一个类型和两个与之相关的函数:

#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;
}

这是该程序的 Objective-C 实现:

#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;
}

纯C程序的输出是:

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

Objective-C 程序的输出是:

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

唯一的区别是 NSLog 在文本之前放置的所有垃圾。功能完全相同。因此,在 C 中,您可以使用类似的方法,但它们实际上只是包含指向结构的指针的函数。

我认为这没有回答您最初的问题,但它确实解决了您似乎一直遇到的一些术语问题。


2
投票

到目前为止给出的答案的另一个选择是使用 Objective-C 运行时提供的

objc_msgSend()
函数。


0
投票

据我所知,有两种选择。您应该根据您的需要选择一个。

简单

第一个是通过 C ABI 兼容函数封装和公开 ObjC API。这样,您将能够从 C 代码调用该函数并保留大部分类型安全性。您可能可以传递对 ObjC 类的引用作为参数

struct objc_class *foo
,但我尚未对此进行测试。

// foobar.m

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

灵活

另一个选择是使用 ObjC 运行时。这样,您就不需要直接编译 Objective C 作为程序的一部分。这种方法的缺点是完全失去类型安全性。此外,您需要做出编译器通常为您做的决定。例如,您需要根据

context
使用
objc_msgSend
objc_msgSend_stret。不要忘记包含
-framework Cocoa
或您需要的任何内容作为编译器标志的一部分。

// 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;
}
© www.soinside.com 2019 - 2024. All rights reserved.