关于@synthesize的问题

Mat*_*ani 14 xcode objective-c

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

@synthesize window=_window;

@synthesize managedObjectContext=__managedObjectContext;
Run Code Online (Sandbox Code Playgroud)

仅使用下划线或双倍之间有什么区别?只写作有什么区别:

@synthesize window;
Run Code Online (Sandbox Code Playgroud)

Jan*_*ano 27

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

考虑差异(非ARC代码):

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
Run Code Online (Sandbox Code Playgroud)

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

@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
Run Code Online (Sandbox Code Playgroud)

更糟糕的是,像Core Data这样的一些API依靠KVC通知来执行延迟加载.如果您不小心跳过了访问者,您的数据将返回为零.

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

  • self.var 访问器引用(调用setter和getters),
  • _var 直接访问参考(跳过设置者和获取者),
  • var无效的参考.

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

继续阅读细节......


现代运行时

在Objective-C 2.0中,您声明如下变量:

@interface User : NSObject
@property (nonatomic, assign) NSInteger age;
@end
@implementation User {
@synthesize age; // this line can be omitted since LLVM 4.0
@end
Run Code Online (Sandbox Code Playgroud)

由编译器翻译如下:

@interface User : NSObject {
    NSInteger age;
}
@end
@implementation User
-(void)setAge:(NSInteger)newAge {
    age=newAge;
}
-(void)age {
    return age;
}
@end
Run Code Online (Sandbox Code Playgroud)

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

@synthesize age=_age;
Run Code Online (Sandbox Code Playgroud)

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

@interface User : NSObject {
    NSInteger _age;
}
@end
@implementation User
-(void)setAge:(NSInteger)newAge {
    _age=newAge;
}
-(void)age {
    return _age;
}
@end
Run Code Online (Sandbox Code Playgroud)

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

访问者合成的行为有所不同,这取决于运行时:

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

  • 对于遗留运行时,实例变量必须已在当前类的@interface块中声明.如果存在与该属性同名的实例变量,并且其类型与属性的类型兼容,则使用它 -否则,您将收到编译器错误.

传统运行时

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

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

@interface User : NSObject {
    NSInteger age;
}
@property (nonatomic, assign) NSInteger age;
@end
@implementation User
@synthesize age;
@end
Run Code Online (Sandbox Code Playgroud)

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

@interface User : NSObject {
    NSInteger _age;
}
@property (nonatomic, assign) NSInteger age;
@end
@implementation User
@synthesize age = _age;
@end
Run Code Online (Sandbox Code Playgroud)

什么是最好的方法?

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

关于方法的Apple:Cocoa的编码指南:印刷约定:

避免使用下划线字符作为私有的前缀,特别是在方法中.Apple保留使用此约定.第三方使用可能导致名称空间冲突; 他们可能会无意中用自己的方法覆盖现有的私有方法,带来灾难性的后果.

Apple on variables:声明的属性和实例变量

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

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

  • 所有以下划线开头的标识符以及大写字母或另一个下划线始终保留用于任何用途.
  • 所有以下划线开头的标识符始终保留用作普通和标记名称空间中具有文件范围的标识符.

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

Google Objective-C风格指南:

变量名称变量名称以小写字母开头,并使用大小写分隔单词.类成员变量具有尾随下划线.例如:myLocalVariable,myInstanceVariable_.如果不允许使用Objective-C 2.0的@property,那么用于KVO/KVC绑定的成员可以从前导下划线开始.

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

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

无论你选择什么,碰撞都不太可能发生,如果他们这样做,你会得到编译器的警告.如有疑问,请使用默认的LLVM方式:@synthesize var=_var;


我对这篇文章进行了编辑,以阅读Mark Dalrymple的伊娃装饰动机.你可能想看一下.