2

在 Objective-C 中,我们可以添加@property@synthesize创建一个属性——比如一个带有 getter 和 setter 的实例变量,它们对这个类的用户是公共的。

在这种情况下,不就和声明一个实例变量并公开它一样吗?这样就不会有调用 getter 和 setter 作为方法的开销。我们可能有机会对 setter 进行验证,例如将数字限制在 0 到 100 之间,但除此之外,公共实例变量不会实现同样的效果,而且速度更快吗?

4

6 回答 6

3

开销不是真正的问题

要回答您的最后一个问题,是的,会有开销——但是再推一个帧并将其从堆栈中弹出的开销可以忽略不计,尤其是考虑到现代处理器的强大功能。如果您非常关心性能,您应该分析您的应用程序并确定实际问题出在哪里——我保证您会找到比删除一些访问器更好的地方进行优化。

这是好设计

封装您的私有成员并使用访问器和修改器保护它们只是良好软件设计的基本原则:它使您的软件更易于维护、调试和扩展。对于任何其他语言,您可能会问同样的问题:例如,为什么不在 Java 类中公开所有字段?(除了像 Ruby 这样的语言,我想,这使得无法公开实例变量)。底线是某些软件设计实践已经到位,因为随着您的软件变得越来越大,您将把自己从真正的地狱中拯救出来。

延迟加载

在 setter 中进行验证是一种可能性,但您可以做的远不止这些。您可以覆盖您的 getter 以实现延迟加载。例如,假设您有一个类必须从文件或数据库中加载一些字段。传统上这是在初始化时完成的。但是,可能并非所有字段都会被实例化对象的人实际使用,因此您需要等待初始化这些成员,直到通过 getter 请求它。这会清理初始化,并且可以更有效地利用处理时间。

有助于避免 ARC 中的保留周期

最后,属性可以更容易地避免在 ARC 下使用块的保留循环。ivars 的问题在于,当您访问它们时,您是在隐式引用 self. 所以,当你说:

_foo = 7;

你真正想说的是

self->_foo = 7;

所以说你有以下几点:

[self doSomethingWithABlock:^{
    _foo = 7;
}];

你现在已经有了一个保留周期。你需要的是一个弱指针。

__block __weak id weakSelf = self;
[self doSomethingWithABlock:^{
    weakSelf->_foo = 7;
}];

现在,显然这仍然是 setter 和 getter 的问题,但是您不太可能忘记使用weakSelf,因为您必须显式调用 self.property,而 ivars 被 self 隐式引用。如果您使用属性,静态分析器将帮助您解决这个问题。

于 2012-04-14T23:40:43.193 回答
3

即使您只使用由 生成的访问器@synthesize,它们也会为您带来以下好处:

  1. 内存管理:生成的设置器保留(retain)属性的新值。如果您尝试直接从类外部访问对象 ivar,您不知道该类是否会保留它。(这在 ARC 下不是问题,但仍然很重要。)

  2. 线程安全访问:生成的访问器默认是原子的,因此您不必担心从多个线程访问属性的竞争条件。

  3. Key-Value Coding & Observation: KVC 提供了在各种场景下对属性的便捷访问。您可以在设置谓词时使用 KVC(例如,用于过滤对象的集合),或使用键路径来获取集合中的属性(例如,包含类对象的字典)。KVO 让程序的其他部分自动响应属性值的变化——这在 Mac 上的 Cocoa Bindings 中被大量使用,在那里你可以将控件绑定到属性的值,也可以在 Core Data 中使用两个平台。

除此之外,属性还提供封装。使用您的类实例的其他对象(客户端)不必知道您是否正在使用生成的访问器——您可以创建自己的访问器来执行其他有用的事情,而无需更改客户端代码。在某些时候,您可能会决定您的类需要对其 ivars 之一的外部更改做出反应:如果您已经在使用访问器,您只需要更改它们,而不是让您的客户开始使用它们。或者,Apple 可以在未来的操作系统版本中使用更好的性能或新功能来改进生成的访问器,并且您的类的其余代码及其客户端都不需要更改。

于 2012-04-14T22:19:26.437 回答
1

你是对的......对于一些非常有限的情况。当它们用于像素、图像和实时音频 DSP(等)代码的内部循环时,属性在 CPU 周期性能方面非常糟糕。对于不太频繁的使用,它们在可读可维护可重用代码方面带来了很多好处。

于 2012-04-15T05:15:32.613 回答
1

@property是公开的事实。它告诉其他类,它们可以获取并可能设置该类的属性。属性不是变量,它们就是字面意思。例如,count是 的一个属性NSArray。它一定是实例变量吗?不。而且你没有理由关心它是否是。

@synthesize创建一个默认的 getter、setter 和实例变量,除非你自己定义了这些东西。这是特定于实现的。这是您的班级选择履行其提供财产的合同义务的方式。这只是提供属性的一种方式,您可以随时更改您的实现,而无需告诉其他任何人。

那么为什么不公开实例变量而不是提供 getter 和 setter 呢?因为这将你的双手束缚在类的实现上。它使其他行为依赖于它的特定编码方式,而不仅仅是您选择为其发布的接口。这很快会创建脆弱且相互依赖的代码,这些代码会崩溃。这是面向对象编程的诅咒。

于 2012-04-14T20:26:28.373 回答
1

因为人们通常会对封装和隐藏数据和实现感兴趣。更容易维护;您必须更改一种实现,而不是全部。实施细节对客户是隐藏的。此外,客户端不必考虑该类是否是派生类。

于 2012-04-14T21:17:15.773 回答
0

@property 和 @synthesize 设置正在获取 getter 和 setter 方法

其他用法是您也可以在其他类中使用该变量

如果您想将变量用作实例变量以及您的自定义 getter 和 setter 方法,您可以这样做,但有时当您设置变量的值并且检索变量的值时有时会变成僵尸,这可能会导致您的应用程序崩溃。

所以这个属性会告诉操作系统在你释放你的类对象之前不要释放对象,希望它有帮助

于 2012-04-14T21:07:56.853 回答