@interface或@implementation中的私人伊娃尔
是否有任何理由在@interface
申报私人伊维尔而不是@implementation
?@interface或@implementation中的私人伊娃尔
我看到这样的代码在互联网上(包括Apple提供的文件):
foo.h中
@interface Foo : NSObject {
@private
id _foo;
}
@end
Foo.m
@implementation Foo
// do something with _foo
@end
的头文件定义了一个类的公共接口,而私人ivar是...好...私人。那么为什么不宣布这样呢?
foo.h中
@interface Foo : NSObject
@end
Foo.m在@implementation
@implementation Foo {
@private
id _foo;
}
// do something with _foo
@end
声明实例变量是对象 - 的最新功能,这就是为什么你看到很多的代码与他们在@interface
- 没有其他选择。
如果您正在使用支持声明的实例变量在执行声明它们有可能是最好的默认编译器 - 只把它们放在接口,如果他们需要被其他人访问。
编辑:在实施申报的其他信息
实例变量隐含隐藏(有效私营)和能见度不能改变 - @public
,@protected
和@private
不产生编译器错误(与目前Clang至少)但被忽略。
如果您需要针对旧系统或Xcode版本的编译器支持,则您会倾向于使用@interface
。
如果您确定不需要向后兼容性,那么最好将它放在@implementation
中。
- 我认为@private是一个很好的默认值。
- 它最大限度地减少了编译时间,并减少了依赖关系,如果你使用它的权利。
- 您可以减少标题顶部的大部分噪音。许多人会为他们的ivars放置#imports,但他们应该使用前向声明作为默认值。所以你可以从标题中删除很多#imports和许多前向声明。
的指令@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:warning:实例变量'y'是@private;这将是一个严重的错误,将来
每进行一次“innapropriate”访问“私有”成员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技巧和肉丸手术。
但它确实存在。祝你好运。
我会远离iOS上的'double';) – 2012-06-27 12:39:54
具体而言,有问题的编译器似乎是Clang> 2.(现有的)GCC不会这样做。 – 2012-01-12 19:18:59
在这种情况下,'Clang'和'LLVM'是一样的,对吧? – 2012-06-27 12:38:07
@ranReloaded - no。有gcc - gcc front&backend,gcc-llvm - gcc前端,llvm后端 - 和clang - clang前端,llvm后端。我只在叮当时进行测试,Josh在其中一个gcc上测试过。 YMMV,只需使用你正在使用的任何编译器就可以看到。 – CRD 2012-06-27 19:10:42