我有一个 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”对象内部,以及如何调用它而不导致错误?非常感谢您的帮助! :)
为了使其工作,您应该像这样定义 C 方法:
void cMethod(id param);
当你调用它时,这样调用它:
cMethod(self);
然后,你就可以写:
[param objcMethod];
在你的
cMethod
。
这是因为
self
变量是自动传递给 Objective-C 方法的特殊参数。由于C方法不享受这个特权,如果你想使用self
你必须自己发送。
请参阅编程指南的方法实现部分了解更多信息。
我知道 Aviad 已经回答了你的问题,但只是添加到信息中,因为这并非无关紧要:
在我的例子中,我需要从我自己没有调用的 C 函数中调用 Objective-C 方法(通过注册全局热键事件触发的 Carbon Event 函数),因此将 self 作为参数传递是不可能的。在这种特殊情况下,您可以这样做:
在您的实现中定义一个类变量:
id thisClass;
然后在你的 init 方法中,将其设置为 self:
thisClass = self;
然后,您可以从类中的任何 C 函数调用 Objective-C 方法,而无需将
self
作为参数传递给函数:
void cMethod([some parameters]) {
[thisClass thisIsAnObjCMethod];
}
C 函数不是“在
self
对象内部”。事实上,什么都不是。
Objective-C 方法有效地将
self
作为隐式参数,在幕后完成了魔法。对于普通 C 函数,它们不与任何类或对象关联,并且没有调用魔法,因此没有 self
。如果您需要它,您需要将其作为参数显式传递给您的 C 函数。
说实话,不存在 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 中,您可以使用类似的方法,但它们实际上只是包含指向结构的指针的函数。
我认为这没有回答您最初的问题,但它确实解决了您似乎一直遇到的一些术语问题。
到目前为止给出的答案的另一个选择是使用 Objective-C 运行时提供的
objc_msgSend()
函数。
据我所知,有两种选择。您应该根据您的需要选择一个。
第一个是通过 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;
}