iOS @synthesize var = _var 变量前置下划线解释
前置下划线是一种为了帮助区分实例变量和访问方法的约定。对于编译器来说它只是一种变量重命名而已。
考虑以下代码的区别(不使用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
无效的引用
考虑到当
是LLVM 4.0自动生成的你可以把它当做Objective-C的默认命名规则。@synthesize
被省略的时候@synthesize var=_var
更详细的请继续阅读...
Modern runtime
在Objective-C 2.0中你可以这样声明变量:
@interface User : NSObject
@property (nonatomic, assign) NSInteger age;
@end
@implementation User {
@synthesize age; //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;
因为有modern runtime,你要做的就这么多。如果你不提供实例变量,编译器会帮你添加一个。下面是将被编译的代码:
@interface User : NSObject {
NSInteger _age;
}
@end
@implementation User
-(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代码:
@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
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
A leading underscore is a naming convention helpful to differentiate between instance variables and accessors. For the compiler it is just a common ivar rename.
Consider the difference (non ARC code):
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
With ARC variables won't be leaked, but it is still wrong to skip the @property attributes:
@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
Even worst, some APIs like Core Data rely on KVC notifications to perform lazy loading. If you accidentally skip the accessors, your data will come back as nil.
This is the reason you often find @synthesize var=_var
, which makes
self.var
an accessor reference (invoking setters and getters),_var
a direct access reference (skipping setters and getters),- and
var
an invalid reference.
Given that @synthesize var=_var
is autogenerated by LLVM 4.0 when@synthesize
is omitted, you can consider this the default naming convention in Objective-C.
Keep reading for details...
Modern runtime
In Objective-C 2.0 you declare variables like this:
@interface User : NSObject
@property (nonatomic, assign) NSInteger age;
@end
@implementation User {
@synthesize age; // this line can be omitted since LLVM 4.0
@end
which is translated by the compiler as follows:
@interface User : NSObject {
NSInteger age;
}
@end
@implementation User
-(void)setAge:(NSInteger)newAge {
age=newAge;
}
-(void)age {
return age;
}
@end
If you prefer to use the underscore convention just add the following:
@synthesize age=_age;
That's all you need because with the modern runtime, if you do not provide an instance variable, the compiler adds one for you. Here is the code that gets compiled:
@interface User : NSObject {
NSInteger _age;
}
@end
@implementation User
-(void)setAge:(NSInteger)newAge {
_age=newAge;
}
-(void)age {
return _age;
}
@end
What happens if you add both the ivar and the @property? If the variable has the same name and type, the compiler uses it instead generating a new variable. Quoting The Objective-C Programming Language > Declared Properties >Property Implementation Directives:
There are differences in the behavior of accessor synthesis that depend on the runtime:
For the modern runtimes, instance variables are synthesized as needed.If an instance variable of the same name already exists, it is used.
For the legacy runtimes, instance variables must already be declared in the @interface block of the current class.If an instance variable of the same name as the property exists, and if its type is compatible with the property’s type, it is used —otherwise, you get a compiler error.
Legacy runtime
But if you need to support the legacy runtime you must either provide an instance variable with the same name and compatible type of the property or specify another existing instance variable in the @synthesize statement.
So the legacy code without underscores would be:
@interface User : NSObject {
NSInteger age;
}
@property (nonatomic, assign) NSInteger age;
@end
@implementation User
@synthesize age;
@end
Or if you prefer the underscore convention:
@interface User : NSObject {
NSInteger _age;
}
@property (nonatomic, assign) NSInteger age;
@end
@implementation User
@synthesize age = _age;
@end
What is the best way?
Apple discourages the use of underscore in methods, but not on variables!.
Apple on methods: Coding Guidelines for Cocoa: Typographic Conventions:
Avoid the use of the underscore character as a prefix meaning private, especially in methods. Apple reserves the use of this convention. Use by third parties could result in name-space collisions; they might unwittingly override an existing private method with one of their own, with disastrous consequences.
Apple on variables: Declared Properties and Instance Variables
Make sure the name of the instance variable concisely describes the attribute stored. Usually, you should not access instance variables directly, instead you should use accessor methods (you do access instance variables directly in init and dealloc methods).To help to signal this, prefix instance variable names with an underscore (_), for example:
@implementation MyClass { BOOL _showsTitle; }
ISO/IEC 9899 7.1.3 Reserved identifiers (aka C99):
- All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
- All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.
On top of that, double leading underscore is traditionally reserved for the vendor of the preprocessor / compiler / library. This avoids the case where you use__block
somewhere in your code, and Apple introduces that as a new non-standard keyword.
Google Objective-C Style guide:
Variable Names Variables names start with a lowercase and use mixed case to delimit words. Class member variables havetrailing underscores. For example: myLocalVariable, myInstanceVariable_. Members used for KVO/KVC bindings may begin with a leading underscore iff use of Objective-C 2.0's @property isn't allowed.
Google's trailing underscore doesn't force you to type one more character before Xcode fires the autocomplete, but you'll realize it is an instance variable slower if the underscore is a suffix.
Leading underscore is also discouraged in C++ (seeWhat are the rules about using an underscore in a C++ identifier?) and Core Data properties (try adding a leading underscore in the model and you'll get "Name must begin with a letter").
Whatever you chose, collisions are unlikely to happen, and if they do, you'll get a warning from the compiler. When in doubt, use the default LLVM way:@synthesize var=_var;