有没有理由在@interface
而不是@implementation
声明私人ivar?
我在互联网上看到这样的代码(包括Apple提供的文档):
foo.h中
@interface Foo : NSObject {
@private
id _foo;
}
@end
Foo.m
@implementation Foo
// do something with _foo
@end
头文件定义了一个类的公共接口,而一个私有的ivar是...... well ... private。那么为什么不这样声明呢?
foo.h中
@interface Foo : NSObject
@end
Foo.m
@implementation Foo {
@private
id _foo;
}
// do something with _foo
@end
答案 0 :(得分:24)
在@implementation
中声明实例变量是Obj-C的最新功能,这就是为什么你在@interface
中看到很多带有它们的代码 - 没有别的选择。
如果您正在使用支持在实现中声明实例变量的编译器来声明它们,则可能存在最佳默认值 - 只有在需要其他人访问时才将它们放在接口中。
修改:其他信息
实现中声明的实例变量隐式隐藏(实际上是私有的),并且 不能更改 - @public
,@protected
和{ {1}}不会产生编译器错误(至少使用当前的Clang)但会被忽略。
答案 1 :(得分:4)
如果您需要针对旧系统或Xcode版本的编译器支持,您会赞成@interface
。
如果您确定不需要向后兼容性,我会说最好将其放在@implementation
中。
答案 2 :(得分:3)
指令@ public,@ protected和@private是 在objective-C中没有绑定,它们是编译器提示 变量的可访问性。 它不会限制您访问它们。
示例:
@interface Example : Object
{
@public
int x;
@private
int y;
}
...
...
id ex = [[Example alloc ] init];
ex->x = 10;
ex->y = -10;
printf(" x = %d , y = %d \n", ex->x , ex->y );
...
gcc编译器吐出:
Main.m:56:1:警告:实例变量'y'是@private;这将是一个很难的错误
Main.m:57:1:警告:实例变量'y'是@private;这将是一个很难的错误
每次“无私”访问“私人”成员y,但无论如何都会编译它。
跑步时你得到
x = 10 , y = -10
因此,您不应该以这种方式编写访问代码,但因为objc是C的超集, C语法工作正常,所有类都是透明的。
您可以将编译器设置为将这些警告视为错误和保释 - 但是Objective-C并未在内部设置这种严格性。动态方法调度必须检查每个调用的范围和权限(slooooowwwww ...),因此除了编译时警告之外,系统还要求程序员尊重数据成员范围。
在Objective-C中获取成员的隐私有几个技巧。 一种方法是确保将类的接口和实现分别放在单独的.h和.m文件中,并将数据成员放在实现文件(.m文件)中。 然后导入标头的文件无法访问数据成员,只能访问类本身。 然后在标头中提供访问方法(或不)。您可以实现setter / getter函数 在实现文件中用于诊断目的,如果你想要,它们将是可调用的, 但不能直接访问数据成员。
示例:
@implementation Example2 :Object
{
//nothing here
}
double hidden_d; // hey now this isn't seen by other files.
id classdata; // neither is this.
-(id) classdata { return [classdata data]; } // public accessor
-(void) method2 { ... }
@end
// this is an "informal category" with no @interface section
// these methods are not "published" in the header but are valid for the class
@implementation Example2 (private)
-(void)set_hidden_d:(double)d { hidden_d = d; }
// You can only return by reference, not value, and the runtime sees (id) outside this file.
// You must cast to (double*) and de-reference it to use it outside of this file.
-(id) hidden_d_ptr { return &hidden_d;}
@end
...
[Main.m]
...
ex2 = [[Example2 alloc] init];
double d = ex2->hidden_d; // error: 'struct Example2’ has no member named ‘hidden_d’
id data = ex2->classdata; // error: 'struct Example2’ has no member named ‘classdata’
id data = [ex2 classdata] // OK
[ex2 set_hidden_d : 6.28318 ]; // warning:'Example2' may not respond to '-set_hidden_d:'
double* dp = [ex2 hidden_d_ptr]; // (SO UGLY) warning: initialization from incompatible pointer type
// use (double*)cast -- <pointer-to-pointer conversion>
double d = (*dp); // dereference pointer (also UGLY).
...
编译器会为这些明目张胆的诡计发出警告,但会继续进行 并且相信你知道自己在做什么(真的吗?),并且你有理由(是吗?)。 好像很多工作?容易出错?耶!宝贝! 尝试重构代码,然后再采用魔术C技巧和肉丸手术。
但它确实如此。祝你好运。