83

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

4

7 回答 7

182

有很多答案,但也有很大的困惑。我会试着整理一些东西(或者增加混乱,我们会看到......)

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

  2. 由于自动合成,您不需要显式合成属性,因为它会由编译器自动合成为

    @synthesize propertyName = _propertyName
    

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

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

      同时提供getter 和 setter 自定义实现时,不会自动合成属性

    • 具有自定义 getter 的只读属性

      为只读属性提供自定义 getter 实现时,不会自动合成

    • @动态的

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

    • @protocol 中声明的属性

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

    • 在类别中声明的属性

      这是@synthesize编译器不会自动插入指令的情况,但也无法手动合成此属性。虽然类别可以声明属性,但它们根本无法合成,因为类别不能创建 ivars。为了完整起见,我将补充一点,仍然可以使用 Objective-C 运行时伪造属性合成

    • 被覆盖的属性(自 clang-600.0.51 以来的新特性,随 Xcode 6 一起提供,感谢 Marc Schlüpmann)

      当你重写超类的一个属性时,你必须显式地合成它

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

除了最后三种情况,一般的理念是,每当您手动指定有关属性的所有信息(通过实现所有访问器方法或@dynamic使用它。

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

于 2013-11-06T20:10:52.987 回答
23

如果您没有显式使用@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是将 an 链接iVar@property. 如果你想为它生成特定的 getter 和 setter。但是在给定的代码中没有iVar,我认为这@synthesize是没用的。但现在我认为新问题是“何时使用iVar?”,对于这个问题,除了“从不”之外,我没有其他回应!

于 2013-11-05T08:33:51.840 回答
16

我什么时候应该@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

换句话说,尽管 synthesize 出于实际目的从来没有必要*,但当您提供 getter/setter 时,它可用于定义支持属性的实例变量。您可以在此处决定要使用哪种表格。

@implementation {}过去,我倾向于在@synthesize.

  1. 改变属性的类型,实例变量的类型就会改变。
  2. 更改其存储限定符(例如,将其设为弱而不是强或强而不是弱)并且存储限定符会更改。
  3. 删除或重命名属性,@synthesize将产生编译器错误。你不会得到杂散的实例变量。

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

于 2013-11-05T17:42:40.383 回答
9

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

@property NSString *name;

Xcode 会自动合成一个 iVar,就好像你已经写了......

@synthesize name = _name;

这意味着您可以使用...访问该物业

self.name;
// or
_name;

两者都可以,但仅self.name实际使用访问器方法。

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

如果你只是覆盖 setter 或者你只是覆盖 getter,你就可以了。但是如果你两者都做,那么编译器将无法理解它,你需要手动合成它。

不过根据经验。

不要制作 iVar。只需使用该属性。不要综合它。

于 2013-11-05T17:47:41.630 回答
1

在协议中声明属性时需要进行属性综合。它不会在实现接口中自动合成。

于 2013-11-06T20:20:08.210 回答
0

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

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

所以现在,在将它们注释掉之后,我检查并替换了每个出现的地方,例如

self.firstAsset 似乎我也可以使用firstAsset,但我发现我经常错过看到“ ”。

于 2014-05-06T15:41:10.963 回答
-1

Xcode 不需要显式@synthesize声明。

如果你不写@synthesize它和做一样:

@synthesize manager = _manager;

示例代码可能已经过时了。他们很快就会更新。

您可以访问您的属性,例如:

[self.manager function];

这是 Apple 推荐的约定。我遵循它,我建议你也这样做!

于 2013-11-05T08:38:36.607 回答