何时在实例变量前加下划线?

30

3
在Cocoa/Objective-C类中,变量前面带下划线的作用是将其标记为私有。虽然这并不会实际限制对变量的访问,但是它向其他开发人员表明该变量是不应该被直接访问或修改的。在编写代码时,要小心使用下划线前缀,因为它可能会与苹果公司保留的前缀冲突。 - Chuck
8个回答

34
很多人在对象内使用这种方法来区分私有变量和公共变量,这是与工作方式完全无关的可选方法。

25
苹果公司保留以 _ 开头的变量供自己使用。请不要这样做。 - Georg Schölly
2
如果可以的话,我会点赞gs的评论。 - Abizern
42
gs 是不正确的,以 _ 开头的实例变量是可以的。但是,以 _ 开头的方法名被苹果公司保留了。 - Chris Hanson
2
我的(有限的)经验是:如果你子类化了苹果的一个类,并添加了一个实例变量,它与超类的一个私有实例变量发生冲突,Xcode通常会警告你(无论下划线是否存在)。我已经提交了这种格式的代码(即,实例变量以下划线开头),到目前为止还没有被拒绝。 - Nicolas Miari
@hs: Chris Hanson 来自苹果公司的,他确实知道,童子军的荣誉。前导下划线保留用于方法名称,而不是实例变量。 (如果你犯规了,编译器会让你诚实。)像@synthesize var = _var;这样的结构也(温和地)指导您有建设性地使用访问器和点符号语法 - Joe D'Andrea
显示剩余5条评论

28

你所看到的是使用下划线来区分实例变量和属性。因此,一个类声明可能会是这样的:

@interface Foo {
  NSString* _label;
  ....
}

@property (nonatomic, retain) NSString* label; // notice: no underline

然后在实现文件中,您将拥有:

@synthesize label=_label; // the property is matched with the ivar
现在,在实现内部,如果你想直接访问实例变量,可以使用_label,但是要通过属性访问器方法(其会处理保留/释放和一堆其他簿记任务)访问它,你应该使用self.label。从外部来看,你总是要通过{object}.label属性进行访问。
另一种方法是不使用下划线,直接使用:
NSString* label;
@property (nonatomic, retain) NSString* label;
...
@synthesize label;

它的效果是相同的,但会使读取代码并尝试跟踪


2
谢谢。就个人而言,我不喜欢那些下划线搞乱了的东西 ;) - Thanks
并非完全如此 - 在许多编程语言中,人们使用"",在Objective-C添加属性之前就已经这样做了。这只是其中一种可能的用法,但还有许多其他用途。有些人喜欢将""用于所有实例变量,或仅用于私有变量,或其他任何情况。个人而言,这也不是我的风格。 - Quinn Taylor
2
我刚刚花了很长时间来追踪一个内存管理问题,因为我使用了计时器而不是 self.timer,导致变量没有被保留。通过这次经历,我强烈建议在所有私有变量前面使用 _。 - Casebash
谢谢您的解释,我自己也感到困惑。现在一切都清楚了。 - Calvin

8

如前所述,_someVar 用于表示变量是私有的。这只是一种惯例问题,实际上并不重要。

另一个用途,在C语言中,_function() 表示函数不具有平台可移植性,而__function() 则表示函数没有编译器可移植性。因此,在标准C库中,有时会看到名称前面带有 _ 或 __ 的变量,这就是这些函数所代表的意思。


我猜这就是 _asm 和 __asm 的来历? - Nicolas Miari

2

有时它用于表示私有变量。更普遍地,它只是意味着“这个变量在某种程度上不同”。


2

这是不是可能......(唤起记忆)...

我模糊地记得读过一份ADC文档,解释了苹果保留使用下划线前缀成员变量的权利?并且第三方开发者被劝阻使用此约定以避免冲突?

|K<


碰撞?当子类化时吗?这可能是有意义的。 - Thanks
6
错误。保留的是下划线前缀的方法名,而不是实例变量名。(Objective-C 不使用术语 "member"。) - Chris Hanson
@chris:非常感谢您为我澄清这个问题。 - kent

2

我使用下划线来表示变量是成员变量,类似于匈牙利命名法中的'm'前缀(我非常厌恶这种命名法,但那是另一个故事)。当然,现在有彩色编码编辑器,但我的观点是,在你输入变量之前,这个前缀让你想到变量是成员/实例,而不仅仅是在后来被编辑器着色时。


1

我选择使用下划线作为实例变量的命名方式,因为我经常遇到以下情况:

@interface MyClass:NSObject
{
    NSUInteger count;
}

@property(nonatomic) NSUInteger count;  

-(id) initWithCount(NSUInteger) count;


@end

(...)

@implementation MyClass
@synthesize count;

-(id) initWithCount(NSUInteger) count
{
    if((self = [super init])){
        self.count = count; // Parameter name collides with ivar name!
    }
    return self;
}

@end

所以我这样做:

@interface MyClass:NSObject
{
    NSUInteger _count;
}

@property(nonatomic) NSUInteger count;  

-(id) initWithCount(NSUInteger) count;

@end

(...)

@implementation MyClass
@synthesize count = _count;

-(id) initWithCount(NSUInteger) count
{
    if((self = [super init])){
        _count = count; // No name collision
    }
    return self;
}

@end

当然,我也可以将参数名称更改为“newCount”或“aCount”(我讨厌那个)。我认为这是一个品味问题。


1
通常,这是为了表示变量不应该被开发人员直接操作。这并不是真正的要求,但如果您无法避免在类中拥有一个不希望被干扰的公共变量,那么这是一个好的实践。

1
在类实现中,有一个 @private 关键字用于实例变量。 - Abizern

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接