每个 ivar 都必须是一个属性吗?

Posted

技术标签:

【中文标题】每个 ivar 都必须是一个属性吗?【英文标题】:Must every ivar be a property? 【发布时间】:2011-06-29 05:01:21 【问题描述】:

我看到它在为 ios 编码时到处都建议使用属性来访问实例变量,因为这对内存管理等有好处。

这个建议不太适合我。我发现使用属性而不是普通的旧 ivars 只需要太多代码,如果您对内存管理感到满意,我并没有真正看到好处。真的那么重要吗?您管理实例变量的方法是什么?

【问题讨论】:

一个词:封装。 Setters/getters 给你买同样的封装。 @bbum 属性只是设置器/获取器。 @Danra 我并没有暗示他们不是。 @bbum 实际上,您确实暗示它们不是一回事。 :) 【参考方案1】:

属性只是语法糖,可以避免你一遍又一遍地编写相同的方法。 有了一个属性,您就有了一个释放旧对象并免费保留新对象的 setter。

【讨论】:

我完全理解属性的概念。我的问题是,当实例变量需要更少的代码时,为什么要始终使用属性而不是实例变量。 KVO 合规性是 bbum 正确强调的另一个重要优势。【参考方案2】:

实际上没有必要为所有 ivars 声明属性。想到几点:

如果一个 ivar 在对象的生命周期内只被分配一次,那么声明一个属性并不会真正获得任何好处。只需在 init 期间保留/复制/分配,然后在 dealloc 期间根据需要释放。 如果要频繁更改 ivar,则声明属性并始终使用访问器将更容易避免内存管理错误。 如果属性和 ivars 是私有的,您可以在 .m 文件而不是 .h 文件的类扩展中声明属性。 面向 iOS 4.0+ 时,如果定义属性并合成访问器,则根本不需要在标头中声明 ivars。

所以我通常使用属性,但对于对象在init 期间分配的NSMutableArray 之类的东西,我将使用一个普通的旧 ivar,因为我永远不会重新分配伊瓦尔。

【讨论】:

如果我有一个 NSMutableArray 作为实例变量,我通常故意不为它定义属性。这是因为,如果将它作为可变数组传递给另一个对象,则该另一个对象可以有效地修改第一个对象的内部状态,而无需告知第一个对象。这会破坏封装。 @DanielDickison 怎么样?我想知道为什么有人在我的 Q 中想要 ivar 的一些原因 :: Why would you use an ivar?【参考方案3】:

虽然丹尼尔的回答是正确的,但我认为它遗漏了重要的一点。即:

我发现改用属性 普通的旧 ivars 需要太多 代码,我真的没有看到 如果你觉得舒服的话,会有好处 内存管理。

好处是一致性;一致的内存管理和一致的行为。

值得注意的是,这两行代码在运行时实际上可能具有截然不同的行为:

iVar = [foo retain];
self.iVar = foo;

第一种是直接设置实例变量,不会有变化通知。第二个通过设置器,因此保留了设置时的任何子类自定义确保属性的任何观察者都收到更改通知

如果您在整个代码中直接使用 ivars(在类内部 - 如果您直接从该实例外部使用实例的 ivars,那么...任何处理您的代码库的承包商应该加倍他们的费率;),那么您必须手动处理更改通知传播(通常通过调用willChangeValueForKey:/didChangeValueForKey明确设计您的应用程序以避免使用依赖于密钥的机制-价值观察。

您说“需要太多代码”。我没看到;在上面两行代码中,点的语法是较少的字符。即使使用传统语法调用 setter 方法也会减少代码。

不要低估集中内存管理的价值;在无数呼叫站点和崩溃城市中的一个意外遗漏。

【讨论】:

万一人们将来来到这里并错过了周围的 cmets,在 iOS 4 中,如果您声明一个属性,则不必声明 ivar。这解决了 Diego 在这里讨论的许多问题。 在 iOS 5(和 4,有警告)中使用 ARC,您也不必声明 dealloc @bbum 我很想就我提出的一个相关问题获得您的反馈,Why would you use an ivar? 别忘了使用 sn-ps。 @property(nonatomic,strong) *;分配快捷方式,你很好 @MattDiPasquale 这是一个品味问题;我有时会在@implementation 中声明 ivars,并且只直接使用它们。有时我在 .m 文件顶部的类扩展中声明 @propertys。有点违和,真的。我会在init 之外使用self.foo =(以及在MRR 中的dealloc)。我在init 中使用_foo = 【参考方案4】:

对于私有字段 - 我建议仅将直接 ivars 用于原始类型(BOOL/int/float 等)是安全的。我发现将与内存管理相关的所有内容包装在属性中是一种很好的做法——即使是很少使用的字段。这种方法的另一个好处是 IDE 通常会以不同的方式突出显示直接 ivars 访问,因此您始终可以很好地分离简单的标量字段和对象类型字段。

与此相反,我会强烈反对类公共接口中的任何直接 ivars。由于语言的动态特性,它可能导致极难发现、本地化和修复的运行时错误。考虑以下层次结构

@interface BaseControl
...
@end

@interface Label : BaseControl
...
@end

@interface Button : BaseControl 
  @public
    BOOL enabled;

@end

还有一个代码sn-p

- (void)enableAllButtons 
    NSArray *buttons = [self getAllButtons];   // expected to contain only Button instances
    for (Button *button in buttons) 
        button->enabled = YES;
    
 

现在假设 -getAllButtons 逻辑中的某个地方出现错误,并且您还会在该数组中返回一些标签 - 因此这些标签类实例将丢失分配的 ivar。可能令人惊讶的事实是 -enableAllButtons 在这种情况下不会崩溃。但此时,那些 Label 实例的内部结构已损坏,而这在其他地方使用时会导致未定义的行为和崩溃。

就像一些流行的内存管理问题(通常是悬空指针)——这类问题很难找到和定位——因为错误的出现通常很遥远(在时间、代码或应用程序流方面) ) 从地方,导致错误。但是对于这个特殊问题,您甚至没有方便的工具(如泄漏/僵尸分析器等)来帮助您定位和修复它 - 即使您学习如何重现它并可以轻松调查错误状态。

显然,如果您使用@property (assign) BOOL enabled;,您将在 -enableAllButtons 中获得易于诊断和修复运行时异常。

【讨论】:

按照 Daniel 的建议,将 ivar 用于 NSMutableArray 怎么样?

以上是关于每个 ivar 都必须是一个属性吗?的主要内容,如果未能解决你的问题,请参考以下文章

ivars 和循环方法

每个活动都必须有一个布局吗?

iOS 开发小记

具有额外 ivars 和方法的核心数据管理对象

核心数据中具有自定义类型的所有属性都必须是关系吗?

每个 Python 函数都必须在最后返回吗? [复制]