本文介绍了关于@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:

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:

Apple on variables: Declared Properties and Instance Variables

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

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:

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 的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 06:49
查看更多