什么时候应该显式使用@synthesize?


80

据我所知,自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在代码中明确添加?


1
示例代码可能很旧。基本上,除非有问题(例如,委托中的属性不会自动合成),否则请使用它
borrrden

1
尝试注释掉@sythesize。如果代码仍然有效,则没有必要。
ThomasW

Answers:


170

答案很多,但也很混乱。我会尝试下订单(或增加混乱,我们将看到...)

  1. 让我们停止谈论Xcode。Xcode是一个IDE。clang是一个编译器。我们正在讨论的此功能称为属性自动综合,它是clang支持Objective-C语言扩展,clang是Xcode使用的默认编译器。
    为了清楚起见,如果您在Xcode中切换到gcc,则不会从此功能中受益(无论从Xcode版本开始)。以同样的方式,如果您使用文本编辑器并在命令行中使用clang进行编译,将。

  2. 感谢自动合成,您无需显式合成属性,因为编译器会自动将其合成为

    @synthesize propertyName = _propertyName
    

    但是,存在一些例外情况:

    • 具有自定义getter和setter的readwrite属性

      当提供两个getter和setter定制实现,该属性将不会自动合成

    • 带有自定义getter的readonly属性

      为readonly属性提供自定义getter实现时,该方法不会自动合成

    • @动态

      使用时@dynamic propertyName,属性不会自动合成(很明显,因为@dynamic@synthesize是互斥的)

    • @protocol中声明的属性

      遵循协议时,协议定义的任何属性都不会自动合成

    • 类别中声明的属性

      在这种情况下,@synthesize编译器不会自动插入指令,但是也无法手动合成此属性。尽管类别可以声明属性,但由于类别无法创建Ivar,因此根本无法进行合成。为了完整起见,我要补充一点,即仍然可以使用Objective-C运行时来伪造属性综合

    • 覆盖的属性(自clang-600.0.51起新,随Xcode 6一起提供,谢谢MarcSchlüpmann)

      当您覆盖超类的属性时,必须显式合成它

值得注意的是,合成属性会自动合成支持的ivar,因此,如果缺少属性综合,则除非明确声明,否则ivar也将丢失。

除后三种情况外,一般的哲学是,每当您手动指定有关属性的所有信息(通过实现所有访问器方法或使用@dynamic)时,编译器都会假定您希望完全控制该属性,并且将禁用自动合成功能。它。

除了上面列出的情况外,显式的唯一其他用途@synthesize是指定其他ivar名称。但是约定很重要,因此我的建议是始终使用默认命名。


如果原始提问者仍在这里,我认为这个答案应该被接受。这是最完整的。
史蒂芬·费舍尔

3
据我所知,类别中指定的属性也不会自动合成。(根本原因是您无法在类别中添加实例变量。)
Martin R

@MartinR,好点。它们不会自动合成,但也不能手动合成,因为@synthesize在类别中是被禁止的(类别中没有ivar,正如您已经提到的)。我会加一个便条。
加布里埃尔·彼得罗内拉

但是,此答案并未解决特定问题:何时应使用@synthesize?总是,从不,只有在满足某些条件时才?
杰夫

21

如果您未明确使用,@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
同意,我已停止使用,@synthesize因为没有理由再这样做了。同样,前导下划线是一个很好的可视标记,使您知道您正在处理属性提供的内存管理和线程安全网(您应跳过initdealloc方法)。
BergQuester

1
问题是,何时需要明确需要进行综合。您根本没有回答。
Fogmeister

1
今天我发现,这不是真实。如果没有@synthesize,则负责创建后备实例变量。有了它,编译器将为您完成。
史蒂芬·费舍尔

1
而且我没有对这项发现投反对票。:)
史蒂芬·费舍尔

1
-1完全丢失了需要显式合成的情况。这也是一种编译器功能,而不是Xcode。
百利·彼得罗内拉

14

什么时候应该@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路由是一个更好的选择,因为它删除了冗余类型,并将后备变量与属性明确关联:

  1. 更改属性的类型,实例变量的类型也会更改。
  2. 更改其存储限定符(例如,使其变弱而不是强或使它变弱而不是弱),并且存储限定符也会更改。
  3. 删除或重命名该属性,@synthesize将生成编译器错误。您最终不会遇到流浪实例变量。

*-我知道一种情况是有必要的,它涉及在多个文件中跨类别拆分功能。如果苹果修复了这个问题,甚至已经修复了,我也不会感到惊讶。


你确定吗?你甚至尝试过吗?我写了许多自定义的setter和getters,并且从未合成我的属性。(我用了您的第一个示例,您说它不起作用)
Marc

是,我确定。代码是从新项目中复制粘贴的。除非您在属性上指定非原子性,否则它将不适用于最新版本的Xcode 。
史蒂芬·费舍尔

1
是的,没错。但是您有99%的时间是非原子的。因此,只有在极少数情况下,您的属性是原子的,并且您实际上需要合成一个自定义的getter / setter。
Marc 2013年

如果你提供了一个自定义的实现setter和getter,那么编译器将假定你正在服用的财产的控制权,也不会合成它。
加布里埃尔·彼得罗内拉

2
尽管我可以理解为什么苹果最初认为这是个好主意,但我在那儿与您同意。真正的麻烦是atomic直到后来才添加为属性说明符。现在,您可能会发现警告标记CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES很有趣。
史蒂芬·费舍尔

7

好的,当您创建属性时...

@property NSString *name;

Xcode会自动综合生成一个iVar,就像您编写的一样。

@synthesize name = _name;

这意味着您可以通过...访问酒店

self.name;
// or
_name;

两种方法都可以,但是只能self.name实际使用访问器方法。

自动合成只有一次不起作用:如果您覆盖了setter和getter方法,那么您将需要合成iVar。

如果您仅覆盖setter或覆盖getter,就可以了。但是,如果您同时进行这两种操作,则编译器将无法理解它,因此您需要手动对其进行综合。

根据经验。

不要做iVars。只需使用该属性。不要合成它。


1
在更多情况下,将不执行自动合成。看看我的答案。
加布里埃尔·彼得罗内拉

@GabrielePetronella您能为普通读者简洁地列出这些案例吗?
Dan Rosenstark

@DanRosenstark这与现在进行编程的任何人都无关。您确实应该使用Swift。而TBH,我认为整个综合问题甚至在ObjC中已不再是问题。除非您使用的代码库已有5年以上的历史。
Fogmeister

@Fogmeister是的,在您在答案中提到的情况下(仍然覆盖设置程序和获取程序),这仍然是一件事情。至于Objective-C不再与“现在进行编程的任何人有关”,请给我的日常工作打电话并告诉他们。还要告诉Facebook,Google和Apple在内部使用Objective-C产生巨大效果。
Dan Rosenstark

我敢肯定,这是不正确的Quora,但无论如何: quora.com/...
丹Rosenstark


0

感谢您澄清这一点。我有一个类似的问题。

@synthesize firstAsset, secondAsset, audioAsset;
@synthesize activityView;

因此,现在,在将它们注释掉之后,我仔细检查了每个事件,例如

self.firstAsset似乎我也可以使用firstAsset,但是我发现我想念“ ”了。


-1

Xcode不需要显式@synthesize声明。

如果您写@synthesize的与做的不一样:

@synthesize manager = _manager;

示例代码可能已经很旧了。他们会尽快更新。

您可以像这样访问属性:

[self.manager function];

这是Apple建议的惯例。我遵循它,建议您也这样做!


2
语句[_manager function]将不会访问该属性,而是将直接访问基础ivar 。
CouchDeveloper 2013年

@CouchDeveloper如果您要挑剔,那应该很精确。该属性由两部分组成,包括ivar。因此,最好[_manager function]不要使用该属性的访问器。
Nikolai Ruhe 2013年

@CouchDeveloper-谢谢!固定它!:)
Sam Fischer

1
@NikolaiRuhe你是正确的尼古拉,我应该更精确一些。;)
CouchDeveloper 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.