【问题标题】:Objective-C convention to prevent "local declaration hides instance variable" warning用于防止“本地声明隐藏实例变量”警告的 Objective-C 约定
【发布时间】:2011-02-02 06:12:39
【问题描述】:

我正在使用以下代码...

-(id) initWithVariableName:(NSString*)variableName withComparisonValue:(NSString*)comparisonValue {

    // super init
    self = [super init];
    if (!self) return nil;

    // set instance variables
    self.mustExist = NO;
    self.reverseCondition = NO;
    self.regularExpression = NO;
    self.variableName = variableName; // generates warning
    self.comparisonValue = comparisonValue; // generates warning

    return self;
}

产生了以下两个警告...

  • “variableName”的本地声明隐藏了实例变量
  • “comparisonValue”的本地声明隐藏了实例变量

是否有处理这些警告的通用或公认约定?

我知道这只是告诉用户他们应该在引用类成员时指定一个实例,但这很烦人。

【问题讨论】:

  • 这只是一个警告。你可以忽略它
  • 在本例中这甚至不是问题,因为您通过点语法访问所有实例变量。即使您使用传统的结构取消引用语法 (self->variableName),也不会有问题。这个警告只是告诉你不能单独使用variableName 来引用实例变量。
  • 没有编译指示可以禁用此警告吗?我讨厌丑陋的代码。

标签: objective-c


【解决方案1】:

我看到这是一个相当老的问题,答案已被接受,但我有更好的解决方案及其代码约定。

约定规定私有变量使用下划线 (_varName) 作为前缀,公共变量(如属性)仅使用名称前缀。

这样你就可以在你的函数中调用相同的变量名了。

例子:

ExampleClass.h

@interface ExampleClass : NSObject
{
    NSString *_varName; //this is not required if you create a property
}

@property (nonatomic, retain) NSString *varName;

- (void)someMethodWithVarName:(NSString *)varName;

@end

ExampleClass.m

#import "ExampleClass.h"

@implementation ExampleClass

@synthesize varName = _varName; //if you don't declare the _varName in the header file, Objective-C does it for you.

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}

- (void)someMethodWithVarName:(NSString *)varName
{
    _varName = varName; //just for example purpose
}

@end

【讨论】:

  • 现在我知道为什么在我读过的directx 书中所有实例变量都带有前缀m。像 mVB、mNumRows 等...
  • 这是其他语言的流行约定。另外我想提一下Apple Coding Guidelines 允许使用下划线字符作为实例变量的前缀(请参阅印刷约定here
【解决方案2】:

很遗憾,没有“好的”方法可以防止此错误。常见的模式是使用稍微笨拙的参数名称,例如

-(id) initWithVariableName:(NSString*)theVariableName 
       withComparisonValue:(NSString*)theComparisonValue {
    self.variableName = theVariableName;
    self.comparisonValue = theComparisonValue;

    return self;
}

【讨论】:

  • 我自己更喜欢 initialVariableName。
  • @codewarrior 哦,好主意。我也更喜欢这样。 Apple 文档经常使用theX,但initialX 更具描述性。
  • 在其他语言中,我看到参数使用 _ like Foo(_variableName, _comparisonValue) { variableName = _variableName; ... }
  • 我通常使用aan 前缀。比如aVariableNameaComparisonValueanX等......
  • Tom:在 Obj-C 中我们经常做相反的事情:用下划线命名 ivars,不带下划线。
【解决方案3】:

如果你的方法真的是一个初始化器,别忘了做你的self = [super init];

- (id) initWith...
{
    self = [super init];
    if (!self) return nil;

    // do stuff

    return self;
}

我个人从未遇到过self 更改为nil 或其他值的情况,但这是Objective-C Initialiser Idiom™。

【讨论】:

  • 我知道有类似的事情要做......但这是真正快速的“填充”代码,尚未真正充实:) 感谢您的提醒 :)跨度>
  • 最好检查 (self) 并在该块中进行代码,而不是使用 2 个返回语句。如果 [super init] 给出一个 nil,self 将是 nil 并且无论如何都会在方法的底部返回。
【解决方案4】:

要么给局部变量一个更具描述性的名称(例如initialVariableName),要么给实例变量一个不同的符号 (例如 myClass_variableName)。在大多数情况下,我更喜欢后者,因为当我使用类内部而不是正确的接口时,它会引起人们的注意。

【讨论】:

    【解决方案5】:

    这在现代 Objective-C 中根本不是问题。在现代 Objective-C 中,属性是自动合成的,并且它们对应的实例变量会得到一个 _ 前缀。

    因此,通过自动综合,您的属性将创建实例变量 _variableName_comparisonValue。在这种情况下不会出现阴影。

    更多信息在blog post


    如果你绝对需要手动合成你的属性,像这样重命名合成的 ivar

    @synthesize variableName = _variableName;
    

    一般情况下,重命名您的方法参数。

    【讨论】:

      【解决方案6】:
      _varName = varName;
      

      您可以只使用这个,但不使用 @synthesize - 每当您想使用该变量时,您只需编写 _*variable name* 即可消除错误

      【讨论】:

        【解决方案7】:

        虽然这是个老问题,但我仍然有一个很好的解决方案来抑制代码中的警告

        -(id) initWithVariableName:(NSString*)variableName withComparisonValue:(NSString*)comparisonValue {
        
            // super init
            self = [super init];
            if (!self) return nil;
        
            // set instance variables
            self.mustExist = NO;
            self.reverseCondition = NO;
            self.regularExpression = NO;
        
        
            #pragma GCC diagnostic push
            #pragma GCC diagnostic ignored "-Wshadow-ivar"
            self.variableName = variableName; // generates warning
            self.comparisonValue = comparisonValue; // generates warning
            #pragma GCC diagnostic pop
        
        
        
            return self;
        }
        

        您可以了解GCC pragma here 并获取警告的警告代码,请转到日志导航器 (Command+7),选择最顶层的构建,展开日志(右侧的“=”按钮),然后滚动到底部,您的警告代码在方括号内,例如[-Wshadow-ivar]


        编辑

        你可以使用铿锵声

        #pragma clang diagnostic push
        #pragma clang diagnostic ignored "-Wshadow-ivar"
        // your code
        #pragma clang diagnostic pop
        

        【讨论】:

        • 谢谢。有时,我只想保持同名。
        • 是的,对于在参数化 ctor 中具有相同名称的遗留 java 用户,建议使用
        【解决方案8】:

        您通常应该在实例变量前加上下划线(例如 _variableName),以避免出现这样的编译器警告。

        否则只需稍微更改方法签名中的名称,没有硬定义的命名约定。

        【讨论】:

        • Apple 不鼓励为此使用下划线,因为他们将这种符号用于私有实例变量。
        • 好点,也许更独特的前缀会更好。
        • @Chuck - Apple 不鼓励使用带有初始下划线(或静态范围变量)的 ivars,但像这样的自动变量应该没问题。由于您没有在私有标头中进行编译,因此您不会收到警告,并且由于您无论如何都没有(或不应该)访问这些 ivar,因此自动范围屏蔽 ivar 不是问题。
        • @Jason Coco:我不知道你的意思是“像这样的自动变量”。答案建议为实例变量添加前缀,所以我只是指出下划线可能不是前缀的最佳选择。
        • @Chuck:你说得对,我看错了答案。我以为他出于某种原因在参数名称前面加了前缀。
        【解决方案9】:

        如果您使用的局部实例变量名称与全局实例变量名称相同,则会出现此警告。

        第一种方法:若要忽略此警告,请使用必须更改局部实例变量名或更改全局实例变量名。

        第二种方法:如果你想使用全局变量然后调用self->variableName

        -(id) initWithVariableName:(NSString*)variableName withComparisonValue:(NSString*)comparisonValue {
        
            // super init
            self = [super init];
            if (!self) return nil;
        
            // set instance variables
            self->variableName = variableName; //point to global variableName
            self->comparisonValue = comparisonValue; //point to global comparisonValue
        
            return self;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-15
          • 1970-01-01
          • 1970-01-01
          • 2012-03-10
          • 1970-01-01
          相关资源
          最近更新 更多