题
作为 Objective-C 的新手(但长期使用 C/++)程序员,我正在寻找有关变量命名约定的建议/建议。
我个人的偏好是对实例变量使用前缀,既是为了函数内的清晰性,也是为了防止函数参数的隐藏。不过,我很喜欢排除前缀的属性(除非您也为属性名称添加前缀,这效果不太好而且看起来很愚蠢)。同样,我可以使用“self.variable”约定,但前提是我将所有内容都设为属性。
那么,考虑到下面的代码,您首选的实例/函数变量命名风格是什么?如果您不介意的话,您如何处理函数参数的阴影?
@interface GridItem : NSObject
{
CGRect _rect;
...
}
@end
-(void) initFromRect:(CGRect)rect
{
_rect = rect;
...
}
干杯!
解决方案
大多数 Cocoa 项目都使用 underbar 作为非IBOutlet
实例变量前缀,并且不使用前缀 IBOutlet
实例变量。
我不使用下划线的原因 IBOutlet
实例变量的优点是,当加载 nib 文件时,如果您有一个用于连接插座的 setter 方法,则将调用该 setter 方法。 然而 这个机制确实 不是 使用键值编码,因此 IBOutlet 的名称带有下划线前缀 (例如 _myField
) 将要 不是 除非设置器的名称与插座完全相同(例如 set_myField:
),这是不标准和粗俗的。
另外,请注意使用诸如 self.myProp
是 不是 与访问实例变量相同。你是 发送消息 当您使用属性时,就像使用括号符号一样 [self myProp]
. 。所有属性所做的就是为您提供一个简洁的语法,用于在一行中指定 getter 和 setter,并允许您综合它们的实现;它们实际上并没有使消息调度机制短路。如果您想直接访问实例变量但要在其前面加上前缀 self
你需要治疗 self
作为指针,例如 self->myProp
这确实是 C 风格的字段访问。
最后,在编写 Cocoa 代码时永远不要使用匈牙利表示法,并避免使用其他前缀,如“f”和“m_”——这会将代码标记为是由不“理解”的人编写的,并会导致它会受到其他 Cocoa 开发人员的怀疑。
一般来说,请遵循以下建议 Cocoa 编码指南 文档位于 苹果开发者连接, ,其他开发人员将能够拿起并理解您的代码,并且您的代码将与使用运行时自省的所有 Cocoa 功能良好地配合。
使用我的约定,窗口控制器类可能如下所示:
// EmployeeWindowController.h
#import <AppKit/NSWindowController.h>
@interface EmployeeWindowController : NSWindowController {
@private
// model object this window is presenting
Employee *_employee;
// outlets connected to views in the window
IBOutlet NSTextField *nameField;
IBOutlet NSTextField *titleField;
}
- (id)initWithEmployee:(Employee *)employee;
@property(readwrite, retain) Employee *employee;
@end
// EmployeeWindowController.m
#import "EmployeeWindowController.h"
@implementation EmployeeWindowController
@synthesize employee = _employee;
- (id)initWithEmployee:(Employee *)employee {
if (self = [super initWithWindowNibName:@"Employee"]) {
_employee = [employee retain];
}
return self;
}
- (void)dealloc {
[_employee release];
[super dealloc];
}
- (void)windowDidLoad {
// populates the window's controls, not necessary if using bindings
[nameField setStringValue:self.employee.name];
[titleField setStringValue:self.employee.title];
}
@end
您会看到我正在使用引用的实例变量 Employee
直接在我的 -init
和 -dealloc
方法,而我在其他方法中使用该属性。这通常是一个很好的模式,具有以下属性:只接触初始化器中属性的底层实例变量, -dealloc
, ,以及属性的 getter 和 setter 中。
其他提示
我遵循 Chris Hanson 关于下划线 ivar 前缀的建议,尽管我承认我也将下划线用于 IBOutlet。然而,我最近开始移动我的 IBOutlet
声明给 @property
线,按照 @mmalc的建议. 。好处是我的所有 ivars 现在都有下划线,并且调用标准 KVC 设置器(即 setNameField:
)。此外,Interface Builder 中的插座名称没有下划线。
@interface EmployeeWindowController : NSWindowController {
@private
// model object this window is presenting
Employee *_employee;
// outlets connected to views in the window
NSTextField *_nameField;
NSTextField *_titleField;
}
- (id)initWithEmployee:(Employee *)employee;
@property(readwrite, retain) Employee *employee;
@property(nonatomic, retain) IBOutlet NSTextField *nameField;
@property(nonatomic, retain) IBOutlet NSTextField *titleField;
@end
您可以在 ivars 上使用下划线前缀,但仍对属性使用非下划线名称。对于合成访问器,只需执行以下操作:
@synthesize foo = _foo;
这告诉编译器使用 the_foo ivar 合成 foo 属性。
如果您编写自己的访问器,那么您只需在实现中使用下划线 ivar 并保留非下划线方法名称。
就我个人而言,我遵循 Cocoa 命名约定,对函数和变量使用驼峰式命名,对对象名称使用大写驼峰式命名(当然没有前导 NS)。
我发现类型前缀使代码对于任何没有编写代码的人来说更加不透明(因为每个人都总是使用不同的前缀),并且在现代 IDE 中,找出某些东西的类型并不是那么困难。
随着属性的引入,我认为不需要在类实例变量前添加“_”前缀。您可以设置一个简单的规则(在头文件中描述),即必须通过属性访问类外部要访问的任何变量,或者使用类上的自定义方法来影响值。对我来说,这似乎比在名字前面加上“_”要干净得多。它还正确封装了这些值,以便您可以控制它们的更改方式。
我不喜欢使用下划线作为任何标识符的前缀,因为 C 和 C++ 都保留某些下划线前缀供实现使用。
我认为使用“self.variable”很丑陋。
一般来说,我对实例变量使用朴素的标识符(即没有前缀或后缀)。如果你的类太复杂以至于你记不住实例变量,那么你就有麻烦了。因此,对于您的示例,我将使用“rect”作为实例变量的名称,并使用“newRect”或“aRect”作为参数名称。
安德鲁:实际上有很多 Cocoa 开发人员根本不使用实例变量前缀。这在 Smalltalk 世界中也非常常见(事实上,我想说在 Smalltalk 中在实例变量上使用前缀几乎是闻所未闻的)。
实例变量的前缀总是让我觉得这是一种 C++ 主义,它被引入了 Java,然后又引入了 C#。由于 Objective-C 世界在很大程度上与 C++ 世界平行,而 Java 和 C# 世界是它的后继者,这可以解释您可能会在不同开发人员群体之间看到的“文化”差异。
我的风格是混合风格,实际上是 PowerPlant 时代的延续:
我使用的最有用的前缀是函数/方法参数的“in”和“out”。这可以帮助您一目了然地知道参数的用途,并且确实有助于防止方法参数和实例变量之间的冲突(您见过多少次参数“表”与同名实例变量冲突)。例如。:
- (void)doSomethingWith:(id)inSomeObject error:(NSError **)outError;
然后我使用实例变量和属性名称的裸名称:
然后我使用“the”作为局部变量的前缀:theTable、theURL 等这再次有助于区分本地变量和实例变量。
然后,在 PowerPlant 样式之后,我使用了一些其他前缀:k 表示常量,E 表示枚举,g 表示全局变量,s 表示静态变量。
我已经使用这种风格大约 12 年了。
虽然我喜欢使用下划线前缀表示 ivars,但我讨厌写作 @synthesize
由于所有重复而导致的行(这不是很 干燥)。我创建了一个宏来帮助完成此操作并减少代码重复。因此,代替:
@synthesize employee = _employee;
我这样写:
ddsynthesize(employee);
这是一个简单的宏,使用标记粘贴在右侧添加下划线:
#define ddsynthesize(_X_) @synthesize _X_ = _##_X_
唯一的缺点是它会混淆 Xcode 的重构工具,并且如果通过重构重命名属性,它不会被重命名。
除了这里所说的之外,请务必阅读有关键值观察兼容命名的 Cocoa 文档。从长远来看,严格遵循这种模式将对你有很大帮助。