我认为如果它是关于从类外部获取/设置一些数据(正确的,应该使用属性),那么这个主题就不那么有争议了。所以在这篇文章中,我们将重点关注私有和受保护的类成员。
有关系吗?
这听起来可能没那么重要——为什么我们不能随意做出决定并坚持下去呢?好吧,properties / ivars 是一直被使用的东西。我认为有时关注我们如何执行常见任务是一种很好的做法。这是很自然的,也是提高效率的好方法——即使是轻微的改进也会在一段时间内产生相当大的影响。
表现
显然,使用属性会消耗一些 CPU,因为会调用其他方法。但这在99.99%的情况下都是微不足道的!对于其余的 0.01%——使用 ivars,但请注意,与您通常可以添加的其他优化相比,您获得的改进几乎没有。
属性
属性有属性,它们非常有用。但是,我认为它们只有在课外使用时才有用。让我们讨论其中的两个:
- readonly – 只读属性意味着它只能由实例设置。为了有用,实例应该在代码中至少设置一次。这意味着您实际上必须写入 ivar。 (有一个例外:您可以在没有 ivar 的情况下实现一个只读属性,但这只是一种方法,不需要 @property 行。)因此,如果您只访问一次 ivar,为什么不在所有地方都使用它呢?
- 复制——有时,你必须复制一些东西——将可变的转换为不可变的或保存一个块。但是对私有属性使用复制属性实际上不会使您的代码更具可读性。如果需要的话,我更喜欢在赋值之前调用复制方法。
如果您在类中使用 ivar / property,您通常知道它的用途是什么以及应该如何访问它。所以我认为属性不会添加任何有利于私有属性的内容。
用自定义实现替换 Getters/Setters
我曾在一个约定不使用 ivars 的团队工作过一次。不要误会我的意思——代码约定通常是一件好事,我只是不同意我对此的主要原因之一:“如果需要,我们可以稍后覆盖默认的 getter/setter”。来吧,你多久覆盖一次内部属性 getter/setter?而且,重构几乎不需要时间,因为所有的用法都在同一个文件中。
访问控制
受保护的成员呢?在目标 C 中,有实例变量的访问控制,但没有方法。一旦你定义了一个方法(或属性),它就可以从任何地方调用(访问),所以理论上只有公共属性。但在我看来,在 .m 中定义一个属性以将其视为私有就足够了。如果你需要保护怎么办?有一个技巧:对于类 SomeClass,您定义一个额外的 .h 文件,将其命名为 SomeClass_protected.h,并将所有受保护的属性放在那里。这有点难看,但这种情况实际上很少见,至少对我而言(我个人认为使用继承更频繁地带来麻烦是有帮助的,而且我会使用它的情况真的很少)。
另一方面,通过使用属性,您可以轻松地在公共和私有之间切换它们(就像已经提到的那样,使用公共 ivars 是非常糟糕的)。在公共和私有之间切换的需要值得怀疑,如果在属性和 ivars 之间切换,重构并不难,但不得不说它掩盖了我的偏见:-)。
四通八达的交通网络
就个人而言,我很确定谁是这里的赢家。让我们看看下面的例子:
[listCopy addObject:self.nextEntry];
_list = listCopy.copy;
查看 nextEntry 和列表。什么是公共的,什么是私人的?虽然 self.nextEntry 是一个属性,但它是私有的——但在检查定义之前您无法知道它!那么 _list 呢?它实际上是一个公共但只读的属性,因此我们通过它的 ivar 访问它以分配一个新值。
我认为这很令人困惑。虽然您可能对 _list 无能为力,但对内部成员使用 ivars 至少会改善我们使用 nextEntry 的情况。它将被称为 _nextEntry,在大多数情况下,这意味着它是私有的。
您当然可以在私有属性中使用 _ 来克服它,尽管我认为我从未见过有人以这种方式使用私有属性。要么这个问题不困扰人们,要么可能是那些被这个问题困扰的人只是使用ivars。
ARC 之前的疼痛
到目前为止,似乎没有比使用私有属性更好的方法了。那为什么开发者要使用它们呢?我认为唯一的原因是非 ARC 历史。在 ARC 之前,您必须保留-释放类中引用的每个对象。私人财产正在解决它真的很好!代替
[listCopy addObject:self.nextEntry];
_list = listCopy.copy;
你会写:
[listCopy addObject:self.nextEntry];
_list = listCopy.copy;
试想一下,如果 ARC 从一开始就存在会发生什么。任何开始使用 Objective C 编程的人都会像在其他语言中一样使用 ivars,因此使用私有属性永远不会成为如此流行的做法。好多了,对吧?