可可Objective-C类中变量前面的下划线如何工作?


157

我在一些iPhone示例中看到,属性在变量前面使用了下划线_。有人知道这意味着什么吗?或如何运作?

我正在使用的接口文件如下所示:

@interface MissionCell : UITableViewCell {
    Mission *_mission;
    UILabel *_missionName;
}

@property (nonatomic, retain) UILabel *missionName;

- (Mission *)mission;

我不确定上面的功能到底是什么,但是当我尝试将任务名称设置为:

aMission.missionName = missionName;

我得到错误:

请求成员“ missionName”使用非结构或联合的形式

Answers:


97

如果您为ivars使用下划线前缀(仅是一个通用约定,而是一个有用的约定),则您需要多做一件事,以便自动生成的访问器(针对该属性)知道要使用哪个ivar。具体来说,在您的实施文件中,您synthesize应如下所示:

@synthesize missionName = _missionName;

更一般而言,这是:

@synthesize propertyName = _ivarName;

78
具有自动合成属性,则不再需要。Xcode在幕后合成了一个@property xxxx和一个名为_xxxx的ivar。整齐。
LearnCocos2D 2012年

@ LearnCocos2D嗨!这里是iOS的新手,我需要澄清一下。在这段时间内,我一直property在.h文件中声明.fie文件,然后self像这样使用.m文件访问它self.someProperty。这是正确的方法吗?还是我应该在代码中使用ivars?
Isuru

设置ivar不会运行属性设置器-您确定对每个特定情况而言这是否是个好主意
LearnCocos2D 2013年

Noob问题:为什么不直接使用ivars?为什么要声明一个单独的var来保存该ivar?
艾伦

1
@Allen,如果我正确理解了您的问题:您声明的单独的var是指向实际变量的指针。这很重要,原因有几个(据我所知)首先,当您将指针传递给函数时,您并没有在复制它的值。您只是在告诉函数在哪里可以找到要使用的值。这有助于使您的已用内存保持较低水平(并且还有助于内存的分配和解除分配,这在没有Java会出现的“垃圾收集”的情况下非常重要)
David Sigley 2013年

18

这只是可读性的约定,对编译器没有任何特殊作用。您会看到人们在私有实例变量和方法名称上使用它。苹果实际上建议不要使用下划线(如果您不小心,则可以覆盖超类中的某些内容),但是您应该对忽略该建议感到满意。:)


19
据我了解,Apple建议不要在方法名称上使用下划线前缀(它们保留它们作为私有方法的约定),但是他们对实例变量名称没有任何建议。
凯兰

9
@Kelan实际上,Apple 鼓励这样做:“通常,您不应该直接访问实例变量,而应该使用访问器方法(您可以直接在init和dealloc方法中访问实例变量)。为表示信号,请添加前缀实例带有下划线(_)的变量名称,例如:\ @implementation MyClass {BOOL _showsTitle;}“
dmirkitanov 2012年

实际上,我不认为Apple鼓励我们这样做,因为iOS开发者库中所有自己的示例代码中都没有()。苹果还表示,他们已经保留了它,这必须意味着他们在内部将其用于自己的框架(如UIKit等)。这就是为什么我们不应该粗心使用它的原因。但是我看到了,在您提供的@kelan链接中。他们实际上在“修订历史记录”中说使用(是“合适的”。我的解释是,我们愿意的话可以“使用”它。
WYS 2013年

苹果的文档它说不要使用方法名称的下划线前缀是在这里
ThomasW

9

我看到的唯一有用的目的是如上所述区分局部变量和成员变量,但这不是必需的约定。与@property搭配使用时,它会增加synthesize语句-的详细程度@synthesize missionName = _missionName;,并且到处都是丑陋的。

与其使用下划线,不如在不冲突的方法中使用描述性变量名。当它们必须冲突时,方法中的变量名称应带有下划线,而不是多个方法可能使用的成员变量。这唯一有用的常见位置是在setter或init方法中。另外,它将使@synthesize语句更简洁。

-(void)setMyString:(NSString*)_myString
{
    myString = _myString;
}

编辑: 利用自动合成的最新编译器功能,我现在为ivar使用下划线(在极少数情况下,我需要使用ivar来匹配自动合成的功能。


相反。强调私有变量。财产没有。并立即合成它们,您将它们耦合。
贾斯汀

这与我所描述的完全一样,只是我将其称为“成员变量”而不是“私有变量”。
Peter DeWeese

哎哟! 这很麻烦……自动综合会生成ivar _myString,这意味着您的设置器将无法工作(因为这样将无法通过method参数告诉ivar)。
geowar

正确,这就是为什么当苹果添加自动合成功能时,我在最后添加了编辑内容的原因。
彼得·德威斯

5

它实际上并没有任何意义,只是一些人用来区分成员变量和局部变量的约定。

至于错误,听起来像是任务的类型错误。它的声明是什么?


在具有智能感知功能的IDE中很常见。它将使您的成员/模块/类变量显示在列表的顶部。另一个常见的前缀是“ m
STW,

1
如果这没有任何意义,您如何像上面的示例一样在_missionName和missionName之间来回切换?我的声明如下:Mission * aMission = [[Mission alloc] init]; aMission.missionName = @“任务”;
Atma

1
一个是实例变量,另一个是属性。您无法使用aMission.missionName之类的语法访问实例变量,因为该语法不适用于指针。
查克(Chuck)

另外,请注意,您正在尝试对Mission对象进行操作,但是您使用MissionName属性发布的接口是MissionCell。
smorgan


1

下划线不仅使无需使用self.member语法即可解析ivars,而且使代码更具可读性,因为您知道什么时候变量是ivar(由于其下划线前缀)或成员参数(无下划线) )。

例:

- (void) displayImage: (UIImage *) image {

    if (image != nil) {
        // Display the passed image...
        [_imageView setImage: image];
    } else {
        // fall back on the default image...
        [_imageView setImage: _image];
    }
}

在这个例子中,很高兴看到self.image(或[self image])的使用比较。什么时候使用self.image更好,什么时候使用_image更好?
Boeckm

2
@Boeckm:通常,您应该使用self.image访问属性。唯一应_image直接访问实例变量的时间是在init方法内,并且dealloc在调用任何其他方法时,该方法都可能会有风险(因为对象是半初始化或半释放的)。
彼得·霍西

1

这似乎是关于self.variableName与_variablename的问题的“主”项目。让我循环的是,在.h中,我有:

...
@interface myClass : parentClass {
className *variableName;    // Note lack of _
}

@property (strong, nonatomic) className  *variableName;
...

这导致self.variableName和_variableName是.m中的两个不同变量。我需要的是:

...
@interface myClass : parentClass {
className *_variableName;    // Note presence of _
}

@property (strong, nonatomic) className  *variableName;
...

然后,在类.m中,self.variableName和_variableName是等效的。

我仍然不清楚的是为什么许多示例仍然有效,即使很难做到这一点。

射线


0

除了下划线,您可以使用self.variable名称,也可以综合变量以使用不带下划线的变量或输出。


2
如果您只需要同一类中的变量,只需在.m文件本身中声明它,那么它将允许您在没有自身或下划线的情况下进行调用
Ansal Antony

0

其他答案缺少的是,使用会_variable阻止您专心输入variable和访问ivar,而不是(假定是预期的)属性。

编译器将强制您使用self.variable_variable。使用下划线使无法键入variable,从而减少了程序员的错误。

- (void)fooMethod {

    // ERROR - "Use of undeclared identifier 'foo', did you mean '_foo'?"
    foo = @1;

    // So instead you must specifically choose to use the property or the ivar:

    // Property
    self.foo = @1;

    // Ivar
    _foo = @1;

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.