@interface或@implementation中的私有ivar

时间:2012-01-12 19:11:41

标签: objective-c visibility instance-variables

有没有理由在@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

3 个答案:

答案 0 :(得分:24)

@implementation中声明实例变量是Obj-C的最新功能,这就是为什么你在@interface中看到很多带有它们的代码 - 没有别的选择。

如果您正在使用支持在实现中声明实例变量的编译器来声明它们,则可能存在最佳默认值 - 只有在需要其他人访问时才将它们放在接口中。

修改:其他信息

实现中声明的实例变量隐式隐藏(实际上是私有的),并且 不能更改 - @public@protected和{ {1}}不会产生编译器错误(至少使用当前的Clang)但会被忽略。

答案 1 :(得分:4)

如果您需要针对旧系统或Xcode版本的编译器支持,您会赞成@interface

如果您确定不需要向后兼容性,我会说最好将其放在@implementation中。

  • 我认为@private是一个很好的默认值。
  • 它可以最大限度地缩短编译时间,并在您正确使用它时减少依赖性。
  • 您可以减少标题顶部的大部分噪音。许多人会将#imports用于他们的ivars,但是他们应该使用前向声明作为默认值。因此,您可以从标题中删除许多#imports和许多前向声明。

答案 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技巧和肉丸手术。

但它确实如此。祝你好运。