【问题标题】:Class variable defined at @implementation rather than @interface?在@implementation 而不是@interface 定义的类变量?
【发布时间】:2010-04-03 14:35:56
【问题描述】:

我是 Objective-C 的新手,但我对一些我在其他任何地方都没有真正看到过的东西感到好奇。

谁能告诉我在@interface 块中声明的私有变量与在类方法之外的@implementation 块中声明的变量有什么区别,即:

@interface Someclass : NSObject {

 NSString *forExample;

}

@end

对比

@implementation Someclass

 NSString *anotherExample;

-(void)methodsAndSuch {}

@end

似乎这两个变量(forExampleanotherExample)在整个班级中都是可访问的,我真的找不到它们的行为有什么不同。第二种形式也叫实例变量吗?

【问题讨论】:

    标签: objective-c


    【解决方案1】:

    后者没有定义实例变量。相反,它是在 .m 文件中定义一个全局变量。这样的变量不是任何对象实例独有的,也不是任何对象实例的一部分。

    这样的全局变量有它们的用途(大致相当于 C++ 静态成员;例如,存储一个单例实例),但通常您会在文件顶部的 @implementation 指令之前定义它们。

    【讨论】:

    • 这是我所怀疑的,你的回答很有意义,但是如果我尝试在任何其他类中访问anotherExample(通过在这些类中正确包含Someclass),编译器会告诉我这个变量未声明。什么给了?
    • @bitcruncher 因为anotherExample 仅对声明它的 .m 文件是全局的。如果您希望它可用于其他文件,则必须在 .h 文件中声明 extern NSString * anotherExample;,然后在需要全局的任何地方声明 #import .h 文件。尽管它确实有有效的用例,但它散发出一股代码味道。
    • 没有实际的代码味道 :-) 这只是一个探索性问题。
    • FWIW,现在可以在 @implementation SomeClass 之后的括号中声明 ivars。我一直忘记它....
    【解决方案2】:

    它们非常不同! @implementation 中的那个是一个全局变量,不是每个实例唯一的。想象一下,这两个变量都有访问器,以显而易见的方式编写。那么行为上的差异就在这里展示了:

    Someclass* firstObject = [[Someclass alloc] init];
    Someclass* secondObject = [[Someclass alloc] init];
    
    //forExample is an instance variable, and is unique to each instance.
    [firstObject setForExample:@"One"];
    [secondObject setForExample:@"Two"];
    NSLog(@"%@",[firstObject forExample]); //Result: "One"
    NSLog(@"%@",[secondObject forExample]); //Result: "Two"
    
    //anotherExample is a global variable, and is NOT unique to each instance.
    [firstObject setAnotherExample:@"One"];
    [secondObject setAnotherExample:@"Two"];
    NSLog(@"%@",[firstObject anotherExample]); //Result: "Two" (!)
    NSLog(@"%@",[secondObject anotherExample]); //Result: "Two"
    
    //Both instances return "Two" because there is only ONE variable this time.
    //When secondObject set it, it replaced the value that firstObject set.
    

    如果您正在寻找这种行为,最好使用类变量,如下所示:

    static NSString* yetAnotherExample = nil;
    

    然后您可以使用类方法与变量进行交互,它显然是特定于类的(而不是特定于实例或全局的)。

    【讨论】:

    • 那么anotherExample 是程序范围的全局变量还是类全局变量?换句话说,anotherExample 是一个始终 存在的全局变量,还是在实例化Someclass 时存在的全局变量?
    • 总是存在。您可以在另一个 .m 文件中声明 extern NSString* anotherExample 并再次使用它。请不要。但你可以。 :P
    • 请不要以为我实际上是这样编码的。 :-) 只是想了解其他人为什么这样做(例如 iPhone 的厄运)
    • 这以清晰的方式解释了不是每个实例唯一的全局变量短语。它值得很多赞成。
    【解决方案3】:

    用一段代码sn-p来区分成员变量和全局变量:

    @implementation MyClass {
      // It is an ivar, or called member variable
      // Can NOT be initialized when defined.
      // Can be accessed with `self->_i`
      int _i; 
    }
    
    - (instancetype)init {
        if (self = [super init]) {
           _i = 2; // should be initialized before being used.
        }
        return self;
    }
    
    int i = 9; // Global variable, and can be initialized when defined.
    - (void)myFun {
        NSLog(@"%i, %i", self->_i, i);
    }
    
    @end
    
    
    // Another file
    
    extern int i;
    NSLog(@"%i", i);
    

    【讨论】:

    • 更新:Apple 更推荐属性。
    【解决方案4】:

    如果您在@implementation 部分中声明一个变量,您实际上是在创建一个全局变量,在任何地方都可见(在您的应用程序的每个方法中)。

    成员变量只能在@interface 部分中声明。它们只能在类本身中访问。

    【讨论】:

      【解决方案5】:

      在我看来,@implementation 块内声明的私有块有点危险,与其他 OOP 概念相比,例如爪哇。它看起来像成员变量,但有点静态。

      新手程序员很容易上当。我编写了一个测试程序并对这种行为感到惊讶。

      @interface SomeClass : NSObject
      {
          NSString *forExample;
      }
      
      - (void) set:(NSString *)one another:(NSString *)another;
      - (void)print;
      
      @end
      

      实施:

      #import "SomeClass.h"
      
      @implementation SomeClass
      
      NSString *anotherExample;
      
      - (void) set:(NSString *)one another:(NSString *)another
      {
          forExample = one;
          anotherExample = another;
      }
      
      - (void)print{
          NSLog(@"One = %@, another = %@", forExample, anotherExample);
      }
      
      @end
      

      测试:

      - (void)testClass {
          SomeClass * s1 = [SomeClass new];
          [s1 set:@"one one" another:@"one another"];
          SomeClass *s2 = [SomeClass new];
          [s2 set:@"two one" another:@"two another"];
          [s1 print];
          [s2 print];
      }
      

      输出是,

      One = one one, another = two another
      One = two one, another = two another
      

      【讨论】:

        【解决方案6】:

        需要明确的是,如果您将 IBOutlet 用于本地化 nib/xib,则永远不要将 IBOutlet 声明为全局变量(在实现中)。

        我花了几个小时弄清楚为什么在任何给定时间只能在一个本地化笔尖中连接插座。

        感谢您提出这个问题和答案!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-07-30
          • 1970-01-01
          • 2020-03-03
          • 1970-01-01
          • 1970-01-01
          • 2012-02-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多