【问题标题】:Private ivar in @interface or @implementation@interface 或 @implementation 中的私有 ivar
【发布时间】:2012-02-09 02:01:56
【问题描述】:

是否有任何理由在 @interface 而不是 @implementation 中声明私有 ivar?

我在整个互联网上都看到了这样的代码(包括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 Foo {
@private
    id _foo;
}

// do something with _foo
@end

【问题讨论】:

    标签: objective-c visibility instance-variables


    【解决方案1】:

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

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

    编辑:附加信息

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

    【讨论】:

    • 具体来说,有问题的编译器似乎是 Clang > 2。(现有)GCC 不会这样做。
    • @ranReloaded - 没有。有 gcc - gcc 前端和后端,gcc-llvm - gcc 前端,llvm 后端 - 和 clang - clang 前端,llvm 后端。我只在 clang 上测试过,Josh 在其中一个 gcc 上测试过。 YMMV,只要用你正在使用的任何编译器试试看。
    【解决方案2】:

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

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

    • 我认为@private 是一个很好的默认设置。
    • 如果使用得当,它可以最大限度地减少编译时间并减少依赖关系。
    • 您可以减少标题顶部的大部分噪音。许多人会为他们的 ivars 放置 #imports,但他们应该使用前向声明作为默认值。因此,您可以从标题中删除许多 #import 和许多前向声明。

    【讨论】:

      【解决方案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;这将是未来的一个硬错误

      “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 技巧和像这样的肉丸手术之前,请先尝试重构您的代码。

      但确实如此。祝你好运。

      【讨论】:

      • 我会远离 iOS 上的double ;)
      猜你喜欢
      • 2011-07-30
      • 2012-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-07
      • 1970-01-01
      • 2015-11-25
      相关资源
      最近更新 更多