关于@synthesize 的问题 [英] Question about @synthesize

查看:31
本文介绍了关于@synthesize 的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当您从 Xcode 创建一个嵌入 CoreData 的新应用程序时,您会在委托的实现文件中获得这些行:

@synthesize window=_window;@synthesize managedObjectContext=__managedObjectContext;

只使用下划线或使用双下划线有什么区别?只写有什么区别:

@synthesize 窗口;

解决方案

前导下划线是一种命名约定,有助于区分实例变量和访问器.对于编译器,它只是一个普通的 ivar 重命名.

考虑差异(非 ARC 代码):

self.date = [NSDate 日期];//OK,setter先释放旧值日期 = [NSDate 日期];//错误,跳过 setter 会导致内存泄漏_date = [NSDate 日期];//错误但更容易看出它不是局部变量

with ARC 变量不会泄露,但是跳过@property 属性还是错误的:

@property(复制)字符串;//...self.string = someString;//好的,字符串被复制字符串 = someString;//错误的字符串被保留但不复制_string = someString;//错误但希望更容易看到

更糟糕的是,一些 API(如 Core Data)依赖 KVC 通知来执行延迟加载.如果您不小心跳过访问器,您的数据将返回 nil.

这就是你经常发现@synthesize var=_var的原因,这使得

  • self.var 一个访问器引用(调用 setter 和 getter),
  • _var 直接访问引用(跳过 setter 和 getter),
  • var 无效引用.

鉴于 @synthesize var=_var 在省略 @synthesize 时由 LLVM 4.0 自动生成,您可以将其视为 Objective-C 中的默认命名约定.>

继续阅读以了解详情...

<小时>

现代运行时

在 Objective-C 2.0 中,您可以像这样声明变量:

@interface 用户:NSObject@property (nonatomic, assign) NSInteger age;@结尾@实现用户{@综合年龄;//从 LLVM 4.0 开始可以省略这一行@结尾

由编译器翻译如下:

@interface 用户:NSObject {NSInteger 年龄;}@结尾@实现用户-(void)setAge:(NSInteger)newAge {年龄=新年龄;}-(无效)年龄{回归年龄;}@结尾

如果您更喜欢使用下划线约定,只需添加以下内容:

@synthesize age=_age;

这就是你所需要的,因为与现代运行时,如果您不提供实例变量,编译器会为您添加一个.这是编译的代码:

@interface 用户:NSObject {NSInteger _age;}@结尾@实现用户-(void)setAge:(NSInteger)newAge {_age=新时代;}-(无效)年龄{返回_年龄;}@结尾

如果同时添加 ivar 和 @property 会发生什么?如果变量具有相同的名称和类型,编译器将使用它而不是生成一个新变量.引用 Objective-C 编程语言 > 声明的属性 > 财产实施指令:

<块引用>

访问器合成的行为存在差异取决于运行时:

  • 对于现代运行时,实例变量是根据需要合成的.如果已经存在同名的实例变量,则为使用.

  • 对于遗留运行时,实例变量必须已经在当前类的@interface 块中声明.如果一个实例与属性同名的变量存在,并且如果它的类型是与属性的类型兼容,它被使用——否则,你得到一个编译器错误.

旧版运行时

但是如果你需要支持遗留运行时 您必须提供一个具有相同名称和属性兼容类型的实例变量,或者在@synthesize 中指定另一个现有实例变量声明.

所以没有下划线的遗留代码是:

@interface 用户:NSObject {NSInteger 年龄;}@property (nonatomic, assign) NSInteger age;@结尾@实现用户@综合年龄;@结尾

或者,如果您更喜欢下划线约定:

@interface 用户:NSObject {NSInteger _age;}@property (nonatomic, assign) NSInteger age;@结尾@实现用户@synthesize 年龄 = _age;@结尾

最好的方法是什么?

Apple 不鼓励在方法中使用下划线,但不鼓励在变量中使用!

Apple 关于方法:可可编码指南:排版约定:

<块引用>

避免使用下划线字符作为前缀表示私有,尤其是在方法上. Apple 储备本公约的使用.使用人第三方可能会导致名称空间冲突;他们可能无意中覆盖了现有的具有自己的私有方法之一,带来灾难性的后果.

Apple 关于变量:声明的属性和实例变量

<块引用>

确保实例变量的名称简洁地描述了属性存储.通常,您不应访问实例变量直接,而不是您应该使用访问器方法(您确实访问实例变量直接在 init 和 dealloc 方法中).帮助用下划线 (_) 表示实例变量名称的前缀,例如:@implementation MyClass { BOOL _showsTitle;}

ISO/IEC 9899 7.1.3 保留标识符(又名 C99):

<块引用>
  • 所有以下划线和大写字母开头的标识符字母或其他下划线是始终保留用于任何用途.
  • 全部以 a 开头的标识符下划线始终保留供使用作为具有文件作用域的标识符普通和标记名称空间.

最重要的是,双前导下划线传统上是为预处理器/编译器/库的供应商保留的.这避免了您在代码中的某处使用 __block 的情况,Apple 将其作为新的非标准关键字引入.

Google Objective-C 风格指南:

<块引用>

变量名变量名开头使用小写并使用混合大小写分隔单词.类成员变量有尾随下划线.为了例如:myLocalVariable,myInstanceVariable_.会员用于KVO/KVC 绑定可能以引导下划线 iff 使用Objective-C 2.0 的 @property 不是允许.

Google 的尾随下划线不会强制您在 Xcode 触发自动完成之前再输入一个字符,但您会意识到如果下划线是后缀,它是一个较慢的实例变量.

在 C++ 中也不鼓励使用前导下划线(请参阅在 C++ 标识符中使用下划线的规则是什么?)和核心数据属性(尝试在模型中添加前导下划线,您将得到名称必须以字母开头").

无论您选择什么,都不太可能发生冲突,如果发生冲突,您将收到编译器的警告.如有疑问,请使用默认的 LLVM 方式:@synthesize var=_var;

<小时>

我对这篇文章进行了编辑以阅读A Motivation for马克·达尔林普尔的 ivar 装饰.你可能想看看.

When you create a new application from Xcode that embed CoreData you got those lines in the implementation file of the delegate:

@synthesize window=_window;

@synthesize managedObjectContext=__managedObjectContext;

What are the differences between using only a underscore or double it? What's the difference on writing only:

@synthesize window;

解决方案

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 have trailing 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++ (see What 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;


I own an edit of this post to reading A Motivation for ivar decorations by Mark Dalrymple. You may want to check it out.

这篇关于关于@synthesize 的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆