是否有理由在@interface
而不是@implementation
中声明私有ivar?
我在互联网上看到了这样的代码(包括苹果提供的文档):
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 Foo {
@private
id _foo;
}
// do something with _foo
@end
在@implementation
中声明实例变量是Obj-C的最新功能,这就是为什么您在@interface
中看到了很多带有实例变量的代码——别无选择。
如果您使用的编译器支持在声明实例变量的实现中声明实例变量,那么可能是最好的默认值——只有在需要其他人访问时才将它们放在接口中。
编辑:附加信息
实现中声明的实例变量隐式隐藏(实际上是私有的),可见性不能更改-@public
、@protected
和@private
不会产生编译器错误(至少使用当前Clang),但会被忽略。
如果需要针对较旧系统或Xcode版本的编译器支持,您会更喜欢@interface
。
如果您确信不需要这种向后兼容性,我认为最好将其放在@implementation
中。
- 我认为@private是一个很好的默认值
- 它最大限度地减少了编译时间,如果使用得当,还可以减少依赖关系
- 你可以在你的页眉顶部减少很多噪音。许多人会将#imports作为他们的ivar,但他们应该使用远期声明作为默认值。因此,您可以从标头中删除许多#导入和许多正向声明
指令@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技巧和像这样的肉丸手术之前,先尝试重构代码。
但它就在那里。祝你好运。