前置下划线是一种为了帮助区分实例变量和访问方法的约定。对于编译器来说它只是一种变量重命名而已。

考虑以下代码的区别(不使用ARC的情况下):

self.date =[NSDate date];// 正确,set方法首先释放原来的值
date =[NSDate date];// 错误,省略set方法将导致内存泄露
_date =[NSDate date];// 错误,但是这样很容易看出来它不是一个局部变量

使用ARC的情况下变量将不会内存泄露,但是省略 @property 标志却是错误的:

@property(copy) string;// ...
self.string = someString;// 正确,string将被复制
string = someString;// 错误,string被保留而不是复制
_string = someString;// 错误,但是这样更容易区别

最坏的情况,Core Data等API是基于KVC消息来实现延迟加载的, 如果你不小心忽略了访问方法,数据将被返回为nil

以下就是为什么使用@synthesize var=_var的原因

  • self.var 通过访问方法的引用(包括set和get方法)
  • _var 直接引用(不通过set或者get方法)
  • var无效的引用

考虑到@synthesize被省略的时候@synthesize var=_var是LLVM 4.0自动生成的你可以把它当做Objective-C的默认命名规则。

更详细的请继续阅读...


Modern runtime

在Objective-C 2.0中你可以这样声明变量:

@interfaceUser:NSObject@property(nonatomic, assign)NSInteger age;@end@implementationUser{@synthesize age;//LLVM 4.0之后这一行可以省略@end

将被编译器解释为:

@interfaceUser:NSObject{NSInteger age;}@end@implementationUser-(void)setAge:(NSInteger)newAge {
    age=newAge;}-(void)age {return age;}@end

如果你更愿意使用前置下划线规则的话,向下面这样:

@synthesize age=_age;

因为有modern runtime,你要做的就这么多。如果你不提供实例变量,编译器会帮你添加一个。下面是将被编译的代码:

@interfaceUser:NSObject{NSInteger _age;}@end@implementationUser-(void)setAge:(NSInteger)newAge {
    _age=newAge;}-(void)age {return _age;}@end

如果你同时添加了ivar和@property将会怎么样呢?如果这个变量有相同的变量名和类型,编译器将直接使用它而不是产生一个新的变量。引用自 The Objective-C Programming Language > Declared Properties >Property Implementation Directives:

由于runtime的关系,访问方法的行为存在不同:

  • 对于modern runtime来说,实例变量在需要的时候生成。如果已经存在一个同名的实例变量,这个已经存在的变量就会被使用。

  • 对于legacy runtime来说,实例变量必须已经在当前类的声明中声明过。如果一个同名实例变量作为属性存在并且它的类型和属性的类型兼容,这个同名实例变量将被使用,否则将发生编译错误。

Legacy runtime

如果要支持legacy runtime,必须提供一个同名并且和属性类型兼容的实例变量或者在@synthesize语句里面指定另外一个已经存在的实例变量

没用下划线的legacy代码:

@interfaceUser:NSObject{NSInteger age;}@property(nonatomic, assign)NSInteger age;@end@implementationUser@synthesize age;@end

使用下划线规则:

@interfaceUser:NSObject{NSInteger _age;}@property(nonatomic, assign)NSInteger age;@end@implementationUser@synthesize age = _age;@end

What is the best way?

苹果公司不提倡方法使用下划线,但是对于变量,苹果公司是提倡使用下划线的。

苹果关于方法的代码规范: Coding Guidelines for Cocoa: Typographic Conventions:

避免使用前置下划线符号区表示私有,尤其是方法。苹果保留了这条约定。第三方使用将引起命名空间冲突,他们可能不小心覆盖一个他们自己的私有方法,这将导致灾难性的后果。

苹果关于变量的代码规范:Declared Properties and Instance Variables

保证实例变量名明确描述了变量的属性。通常情况下,你不应该直接访问变量而是应该通过访问方法(在init和dealloc方法中,你直接访问变量)。为了帮助标明这一点,在实例变量前加上下划线, 例如:@implementation MyClass { BOOL _showsTitle; }

ISO/IEC 9899 7.1.3 Reserved identifiers (aka C99):

  • 所有以下划线开始接一个大写字母或者另一个下划线的标示符将被保留。
  • 所有以下划线开始的标示符将被保留用作文件作用域标示符无论是普通还是标记命名空间。

最重要的是,双下划线开头在传统上保留用作预处理/编译器/库。这防止你在代码中使用__block,苹果将此引入作为一个新的非标准关键字。

Google Objective-C Style guide:

变量名小写字母开头,混合使用大小写来区分单词。类成员变量加后置下划线。例如: myLocalVariable, myInstanceVariable_. 类成员用作KVO/KVC绑定的可以使用前置下划线当且仅当它无法使用Objective-C 2.0 的 @property

Google的后置下划线并没有使你在Xcode开始自动完成之前多输入一个字符,但是如果下划线写在末尾,你将慢慢发现它原来是一个实例变量。

C++和Core Data属性不提倡使用前置下划线(参见What are the rules about using an underscore in a C++ identifier?)(尝试在model中添加一个前置下划线你将看到"Name must begin with a letter").

无论你怎么选择都不太可能发生冲突,当发生冲突的时候编译器会给出警告。当你犹豫不决的时候,使用默认的LLVM规范:@synthesize var=_var;


 

原文出自:http://stackoverflow.com/questions/6112283/question-about-synthesize

posted on 2014-04-21 14:42  无涯之径  阅读(204)  评论(0)    收藏  举报