据我所知,自XCode 4.4起,@synthesize
将自动生成属性访问器。但是刚才我已经阅读了有关NSUndoManager
的代码示例,并在代码中注意到@synthesize
已明确添加。像:
@interface RootViewController ()
@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@property (nonatomic, strong) NSUndoManager *undoManager;
@end
@implementation RootViewController
//Must explicitly synthesize this
@synthesize undoManager;
我现在感到困惑......我什么时候应该明确地将@synthesize
添加到我的代码中?
答案 0 :(得分:152)
有很多答案,但也有很大的困惑。我会尝试下订单(或增加一些混乱,我们会看到......)
让我们停止谈论Xcode。 Xcode是 IDE 。 clang是编译器。我们讨论的这个特性叫做属性的自动合成,它是一个Objective-C language extension supported by clang,它是Xcode使用的默认编译器。
为了说清楚,如果你在Xcode中切换到gcc,你将无法从这个功能中受益(无论是Xcode版本。)同样如果你使用文本编辑器并使用命令行中的clang编译,你将
感谢自动合成,您不需要显式合成属性,因为它将由编译器自动合成为
@synthesize propertyName = _propertyName
但是,存在一些例外情况:
使用自定义getter和setter的readwrite属性
当提供两者 getter和setter自定义实现时,该属性将不会自动合成
使用自定义getter的readonly属性
为readonly属性提供自定义getter实现时,不会自动合成
<强> @dynamic 强>
使用@dynamic propertyName
时,该属性不会自动合成(非常明显,因为@dynamic
和@synthesize
是互斥的)
在@protocol
中声明的属性当符合协议时,协议定义的任何属性都不会自动合成
在类别中声明的属性
这种情况下编译器不会自动插入@synthesize
指令,但也无法手动合成此属性。虽然类别可以声明属性,但它们根本无法合成,因为类别无法创建ivars。为了完整起见,我将补充一点,它仍然可以to fake the property synthesis using the Objective-C runtime。
已覆盖的属性(自clang-600.0.51以来的新版本,随Xcode 6发布,感谢MarcSchlüpmann)
当覆盖超类的属性时,必须明确地合成它
值得注意的是,合成属性会自动合成背景ivar,因此如果缺少属性合成,除非明确声明,否则ivar也会丢失。
除了最后三种情况,一般的理念是,无论何时手动指定有关属性的所有信息(通过实现所有访问器方法或使用@dynamic
),编译器都会假定您希望完全控制属性,它将禁用它上面的自动合成。
除了上面列出的情况之外,显式@synthesize
的唯一其他用途是指定不同的ivar名称。但是约定很重要,所以我的建议是始终使用默认命名。
答案 1 :(得分:17)
如果您没有明确使用@synthesize
,编译器会以相同的方式理解您的属性
@synthesize undoManager=_undoManager;
然后您就可以在代码中写下以下内容:
[_undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter
这是常见的惯例。
如果你写
@synthesize undoManager;
你将拥有:
[undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter
我个人不再使用@synthesize
,因为它不再是强制性的。
对我而言,使用@synthesize
的唯一原因是将iVar
与@property
相关联。如果你想为它生成特定的getter和setter。
但是在给定的代码段中没有iVar
,我认为这个@synthesize
是没用的。但是现在我认为新的问题是“何时使用iVar
?”,而我对此没有其他回应而不是“永远”!
答案 2 :(得分:11)
我应该何时将@synthesize
明确添加到我的代码中?
一般情况下,如果需要:您可能永远不会遇到需要的情况。
但是有一种情况你可能会发现它很有用。
假设您正在编写自定义getter和setter,但需要一个实例变量来支持它。 (对于原子属性,这就像想要自定义setter一样简单:如果为单原子属性指定了setter而不是原子属性,编译器将编写一个getter。)
考虑一下:
@interface MyObject:NSObject
@property (copy) NSString *title;
@end
@implementation MyObject
- (NSString *)title {
return _title;
}
- (void)setTitle:(NSString *)title {
_title = [title copy];
}
@end
这不起作用,因为_title
不存在。您已经指定了getter或setter,因此Xcode(正确)不会为它创建支持实例变量。
让它存在有两种选择。您可以将@implementation
更改为此:
@implementation MyObject {
NSString *_title;
}
- (NSString *)title {
return _title;
}
- (void)setTitle:(NSString *)title {
_title = [title copy];
}
@end
或者将其更改为:
@implementation MyObject
@synthesize title = _title;
- (NSString *)title {
return _title;
}
- (void)setTitle:(NSString *)title {
_title = [title copy];
}
@end
换句话说,虽然合成是出于实用目的而不必要*,但是当您提供getter / setter时,它可以用于定义属性支持实例变量。您可以决定要使用哪种表格。
过去,我倾向于在@implementation {}
中指定实例变量,但我现在认为@synthesize
路由是更好的选择,因为它删除了冗余类型并明确地绑定了支持变量到酒店:
@synthesize
将生成编译器错误。您不会最终得到杂散的实例变量。* - 我知道有必要的一个案例,涉及跨多个文件中的类别拆分功能。如果Apple修复此问题,甚至已经修复过,我也不会感到惊讶。
答案 3 :(得分:6)
好的,当你创建一个属性......
@property NSString *name;
Xcode将自动合成iVar,就像你写的那样......
@synthesize name = _name;
这意味着您可以使用...
访问该媒体资源self.name;
// or
_name;
要么可以工作,但只有self.name
实际使用访问者方法。
只有一次自动合成不起作用:如果你覆盖了setter和getter方法,那么你将需要合成iVar。
如果你只是覆盖了setter,或者只是覆盖了getter,那你就没问题了。但如果您同时执行这两项操作,则编译器将无法理解它,您需要手动合成它。
虽然是经验法则。
不要制作iVars。 只需使用该物业。 不要合成它。
答案 4 :(得分:1)
在协议中声明属性时,需要属性合成。它不会在实现界面中自动合成。
答案 5 :(得分:0)
感谢您澄清。我有类似的问题。
@synthesize firstAsset, secondAsset, audioAsset;
@synthesize activityView;
所以现在,在对它们进行评论后,我经历了并用例如
替换了每一个事件self.firstAsset似乎我也可以使用 firstAsset,但我发现我错过了&#34; &#34;太频繁。
答案 6 :(得分:-1)
Xcode不需要明确的@synthesize
声明。
如果你不写@synthesize
,那就像在做:
@synthesize manager = _manager;
示例代码可能已经过时了。他们很快就会更新。
您可以访问以下属性:
[self.manager function];
这是Apple的推荐惯例。我遵循它,我建议你也这样做!