码迷,mamicode.com
首页 > 移动开发 > 详细

iOS @synthesize var = _var 变量前置下划线解释

时间:2015-08-26 07:02:38      阅读:393      评论:0      收藏:0      [点我收藏+]

标签:

本文转载至 http://blog.csdn.net/ztp800201/article/details/9231617 

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

考虑以下代码的区别(不使用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中你可以这样声明变量:

@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;

iOS @synthesize var = _var 变量前置下划线解释

标签:

原文地址:http://www.cnblogs.com/Camier-myNiuer/p/4759165.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!