我什么时候应该明确使用@synthesize?

罗泽轩*_*罗泽轩 69 properties objective-c synthesize

据我所知,由于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;
Run Code Online (Sandbox Code Playgroud)

我现在感到困惑...... @synthesize我什么时候应该明确地添加到我的代码中?

Gab*_*lla 158

有很多答案,但也是一个很大的混乱.我会尝试下订单(或增加一塌糊涂,我们会看到......)

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

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

    @synthesize propertyName = _propertyName
    
    Run Code Online (Sandbox Code Playgroud)

    但是,存在一些例外:

    • 使用自定义getter和setter的readwrite属性

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

    • readonly属性与自定义getter

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

    • @动态

      使用时@dynamic propertyName,属性不会自动合成(非常明显,因为@dynamic并且@synthesize互相排斥)

    • 在@protocol中声明的属性

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

    • 在类别中声明的属性

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

    • 被覆盖的属性(自clang-600.0.51之后的新版本,随Xcode 6发布,感谢MarcSchlüpmann)

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

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

除了最后三种情况,一般的理念是,无论何时手动指定有关属性的所有信息(通过实现所有访问器方法或使用@dynamic),编译器都会假定您想要完全控制属性,它将禁用自动合成它.

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

  • 在我记忆中,类别中指定的属性也不会自动合成.(根本原因是您无法在类别中添加实例变量.) (2认同)

KID*_*dAe 18

如果您没有明确使用@synthesize,编译器将以相同的方式理解您的属性

@synthesize undoManager=_undoManager;
Run Code Online (Sandbox Code Playgroud)

然后你就可以在你的代码中写下这样的东西:

[_undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter
Run Code Online (Sandbox Code Playgroud)

这是常见的惯例.

如果你写

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

你将会有 :

[undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter
Run Code Online (Sandbox Code Playgroud)

我个人不再使用@synthesize,因为它不再是强制性的.对我来说,使用的唯一理由@synthesize是将a链接iVar到a @property.如果你想为它生成特定的getter和setter.但是在给定的代码中没有iVar,我认为这@synthesize没用.但是现在我认为新的问题是"何时使用iVar?",而且我没有其他回应而不是"从不"这个问题!

  • 同意,我不再使用 `@synthesize`,因为没有理由再使用它了。此外,前导下划线作为一个很好的视觉标志,让您知道您正在解决属性提供的内存管理和线程安全网(您应该在 `init` 和 `dealloc` 方法中跳过)。 (2认同)

Ste*_*her 13

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

这不起作用,因为_title不存在.您已经指定了getter或setter,因此Xcode(正确)不会为它创建支持实例变量.

在此输入图像描述

你有两种选择让它存在.您可以更改@implementation为:

@implementation MyObject {
    NSString *_title;
}

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end
Run Code Online (Sandbox Code Playgroud)

或者将其更改为:

@implementation MyObject

@synthesize title = _title;

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end
Run Code Online (Sandbox Code Playgroud)

换句话说,虽然合成是出于实际目的而永远不需要*,但是当您提供getter/setter时,它可用于定义属性支持实例变量.您可以决定要使用哪种表单.

在过去,我更喜欢在中指定实例变量@implementation {},但我现在认为@synthesize路由是更好的选择,因为它删除了冗余类型并明确地将后备变量绑定到属性:

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

* - 我知道有必要的一个案例,涉及跨多个文件中的类别拆分功能.如果Apple修复此问题,甚至已经修复过,我也不会感到惊讶.

  • 我同意你的意见,但我能理解为什么Apple最初认为这是一个好主意.真正的痛苦是`atomic`直到后来才被添加为属性说明符.现在它就在那里,您可能会发现警告标志"CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES"很有趣. (2认同)

Fog*_*ter 6

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

@property NSString *name;
Run Code Online (Sandbox Code Playgroud)

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

@synthesize name = _name;
Run Code Online (Sandbox Code Playgroud)

这意味着您可以使用...访问酒店

self.name;
// or
_name;
Run Code Online (Sandbox Code Playgroud)

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

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

如果您仅覆盖setter或覆盖getter,就可以了。但是,如果两者都做,那么编译器将无法理解它,而您将需要手动对其进行综合。

根据经验。

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