当你从Xcode创建一个嵌入了CoreData的新应用程序时,你会在委托的实现文件中得到这些行:
@synthesize window=_window;
@synthesize managedObjectContext=__managedObjectContext;
使用单个下划线和双个下划线有什么区别?仅写一个下划线和写两个下划线有何不同:
@synthesize window;
当你从Xcode创建一个嵌入了CoreData的新应用程序时,你会在委托的实现文件中得到这些行:
@synthesize window=_window;
@synthesize managedObjectContext=__managedObjectContext;
使用单个下划线和双个下划线有什么区别?仅写一个下划线和写两个下划线有何不同:
@synthesize window;
前导下划线是一种命名约定,有助于区分实例变量和访问器。对于编译器来说,这只是一个常见的ivar重命名。
考虑以下差异(非ARC代码):
self.date = [NSDate date]; // OK, the setter releases the old value first
date = [NSDate date]; // WRONG, skipping the setter causes a memory leak
_date = [NSDate date]; // WRONG but easier to see it's not a local variable
使用ARC,变量不会泄漏,但跳过@property属性仍然是错误的:
@property (copy) string;
// ...
self.string = someString; // OK, string is copied
string = someString; // WRONG string is retained but not copied
_string = someString; // WRONG but hopefully easier to see
@synthesize var=_var
,它使:self.var
成为访问器引用(调用设置器和获取器),
- _var
成为直接访问引用(跳过设置器和获取器),
- 而var
则成为无效引用。@synthesize var=_var
在省略@synthesize
时由LLVM 4.0自动生成,因此您可以将其视为Objective-C中的默认命名约定。在Objective-C 2.0中,您可以这样声明变量:
@interface User : NSObject
@property (nonatomic, assign) NSInteger age;
@end
@implementation User {
@synthesize age; // this line can be omitted since LLVM 4.0
@end
编译器将其翻译如下:
@interface User : NSObject {
NSInteger age;
}
@end
@implementation User
-(void)setAge:(NSInteger)newAge {
age=newAge;
}
-(void)age {
return age;
}
@end
@synthesize age=_age;
这就是你所需要的,因为在现代运行时中,如果你没有提供实例变量,编译器会为你添加一个。以下是编译后的代码:
@interface User : NSObject {
NSInteger _age;
}
@end
@implementation User
-(void)setAge:(NSInteger)newAge {
_age=newAge;
}
-(void)age {
return _age;
}
@end
@interface User : NSObject {
NSInteger age;
}
@property (nonatomic, assign) NSInteger age;
@end
@implementation User
@synthesize age;
@end
或者如果您更喜欢下划线约定:
@interface User : NSObject {
NSInteger _age;
}
@property (nonatomic, assign) NSInteger age;
@end
@implementation User
@synthesize age = _age;
@end
苹果不建议在方法中使用下划线,但变量可以使用!
苹果关于方法的规定:Cocoa编码指南:排版约定:
避免使用下划线作为表示私有的前缀,尤其是在方法中。苹果保留了这种惯例。第三方使用可能会导致命名空间冲突;他们可能会无意中用自己的方法覆盖一个现有的私有方法,造成灾难性后果。
苹果关于变量的规定:声明属性和实例变量
确保实例变量的名称简洁地描述存储的属性。通常,您不应直接访问实例变量,而应使用访问器方法(在init和dealloc方法中,确实需要直接访问实例变量)。为了帮助表明这一点,应将实例变量名以下划线(_)作为前缀,例如:
@implementation MyClass { BOOL _showsTitle; }
ISO/IEC 9899 7.1.3 保留标识符(也称为 C99):
- 所有以下划线和大写字母或另一个下划线开头的标识符始终保留任何用途。
- 所有以下划线开头的标识符始终保留为普通名称空间和标记名称空间中具有文件范围的标识符。
除此之外,双下划线前缀通常保留给预处理器/编译器/库的供应商。这避免了您在代码中使用 __block
,而 Apple 将其引入作为新的非标准关键字的情况。
变量名 变量名以小写字母开头,并使用混合大小写来分隔单词。类成员变量带有尾部下划线。例如:myLocalVariable、myInstanceVariable_。用于 KVO/KVC 绑定的成员变量可以以前导下划线开头,如果不允许使用 Objective-C 2.0 的 @property。
Google 的尾部下划线不会强制您在 Xcode 启动自动完成之前再输入一个字符,但是如果下划线是后缀,您会更慢地意识到它是实例变量。
在C++中,使用前导下划线也是不鼓励的(参见C++标识符中使用下划线的规则是什么?),在Core Data属性中也是一样(尝试在模型中添加前导下划线,你会得到“名称必须以字母开头”的错误信息)。
无论你选择什么方式,冲突的可能性都很小,如果确实发生了冲突,编译器会给出警告。当有疑问时,可以使用默认的LLVM方式:@synthesize var=_var;
我对Mark Dalrymple的文章A Motivation for ivar decorations进行了编辑,建议你去看看。
如果你的实例变量名为'window',那么你可以只使用
@synthesize window;
然而,有些人使用在所有实例变量前缀加下划线的命名约定,但仍然希望其getter和setter没有下划线前缀,这就是'window=_window'的含义。
我不知道双下划线的含义,但我猜测这也是一种命名约定的问题。