从C语言调用Cocoa API

17

我不确定这是否可能,但是在纯C代码中,是否可以调用Cocoa API?
类似于#include <cocoa.h>,添加相应的库并进行操作吗?

感谢帮助。

2个回答

18
如果你使用 Cocoa 或 Foundation 框架,可以在 C 代码中使用 Objective-C。但是,如果想要使用正常的消息语法,则需要将文件扩展名从 .c 改为 .m,以便将其编译为 Objective-C。如果保留 .c 扩展名,则只能使用 c-style 运行时调用来与 Objective 对象交互(例如 objc_msgSend, objc_getClass 等)。
示例:在 .m 文件中。
void cFunction() {
    [[NSString alloc] init];
}

在一个 .c 文件内

void cFunction() {
    void* cls = objc_getClass("NSString");
    void* obj = objc_msgSend(cls, NSSelectorFromString(CFSTR("alloc")));
    obj = objc_msgSend(obj, NSSelectorFromString(CFSTR("init")));
}
如果您选择第二种方法,请参见Objective-C Runtime参考文档

5
通常,直接调用 objc_msgSend 是错误的做法。你应该将其转换为适当的函数类型:typedef CFStringRef (*StringWithStringIMP)(id, SEL, CFStringRef); CFStringRef string = ((StringWithStringIMP)objc_msgSend)(objc_getClass("NSString"), sel_getUid("foo"), CFSTR("This is pointless.")); - Jens Ayton
很酷。我该怎么做?我如何链接Cocoa或Foundation框架?这是在项目级别吗?我可以使用#include吗?我希望继续只有普通的C文件和一个Makefile来编译和链接它(不依赖于XCode来完成)。 - Mr Aleph
1
@MrAleph 你需要告诉GCC链接框架。使用-Xlinker -framework -Xlinker Foundation来实现。你应该使用#include <Foundation/NSObjcRuntime.h>来获取当前运行时头文件。 - ughoavgfhw
3
@MrAleph: 我强烈不建议在 .c 文件中使用 objc_msgSend。只需使用 .m 文件即可。您可以通过使用 gcc foo.m -o foo.o -framework Foundation -framework Cocoa 等方式,在没有 XCode 的情况下编译 .m 文件。 - Yuji

5

Objective-C是在C语言之上的一个非常薄的封装。编译器只是将代码翻译成C语言。

 [obj message:argument];

转化为 C 调用

 obj_msgSend(obj,@selector(message:),argument);

这就是全部内容(其中 @selector(message:) 是一种魔术编码,将选择器(方法名)转换为计算机可理解的东西)。

因此,从编译器的角度来看,Objective-C 和 C 之间没有太大的区别。例如,您可以使用 Objective-C 编译器编译纯 C 程序,得到与使用 C 编译器编译相同的结果。

因此,“混合”一些 Objective-C 和 C 的最简单方法是使用 .m 扩展名,这样编译器就会使用 Objective-C。

这不会使您的程序突然变得非常 Objective-C 风格。您可以保持几乎纯 C 的程序,只需使用扩展名 .m 即可轻松添加一些 Objective-C 消息调用。


我能否编写一个简单的Makefile来编译和链接它? - Mr Aleph
这实际上只是具有不同后缀和不同编译器调用的源文件。创建的目标文件只是纯C编译器创建的普通目标文件。 - yeoman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接