何时使用实例变量,何时使用属性

5
在使用Objective-C属性时,您是否可以完全停止创建实例变量,或者显式实例变量(不是由属性合成的变量)仍然有用途,在属性不合适的情况下?
4个回答

13

你不能完全停止创建实例变量(instance variables),但如果你有属性,可以停止声明它们。如果你合成了一个属性(synthesize a property),并且没有声明过实例变量,那么它将被为你声明,因此你正在创建实例变量,只是不显式地创建。

曾经的建议是为所有东西创建属性,因为使用合成的属性几乎可以为您完成所有的保留和释放操作。然而,随着ARC的出现,使用属性来封装内存管理的原因已经消失了。现在(对于ARC),建议使用属性来声明您的外部接口,但在变量是对象的内部状态的情况下,使用直接实例变量。

这是采用ARC的好理由:属性回归到它们真正的目的,即成为类的API的一部分,而不再需要将其用作隐藏内存管理工作的hacky方法。

编辑

还有一件事:现在可以在@implementation中声明实例变量,因此现在无需在@interface中泄漏任何实现细节。

@implementation MyClass
{
    NSString* myString;
}
// method definitions
@end

我非常确定它在分类中也有效。 - 请参见下面的评论


3
你不能在分类中添加实例变量和属性,因为 Objc 运行时仅允许在注册类时添加实例变量。在将类注册到运行时后,无法更改其内存布局。 - Jonathan Cichon
可以在类扩展中添加ivars(与类别密切相关,但不同)。 - Sean
1
@Jonathan Cichon:谢谢您,我刚试了一下,发现您对实例变量是正确的,但是对属性是错误的。您可以添加属性,但是无法使用“@synthesize”来合成它们。 - JeremyP
2
@JonathanCichon,类别中的非合成属性非常有用且常见。您只需要编写自定义getter / setter即可。请查看此博客文章,了解我最常使用的此技术:http://oleb.net/blog/2011/05/faking-ivars-in-objc-categories-with-associative-references/ - Rob Napier
1
@Jonathan Cichon:这是一个误解,关于属性的。属性是API的一部分,实现取决于你,而@synthesize只是通过添加标准的访问器集合来自动化最常见的实现模式之一。没有什么能阻止你定义自己不同的访问器。 - JeremyP
显示剩余2条评论

10

我建议将所有内容声明为属性,避免手动创建实例变量。手动创建实例变量没有任何好处。在你的头文件 @interface 中声明公共属性,在 .m 文件中的私有类扩展中声明私有属性。

对于JeremyP所说的一些观点,尽管内存管理不再是一个重要问题,但访问器的内部使用仍然具有显着的价值。它确保 KVO 正常工作,更好地支持子类,支持自定义设置器(特别是对于像 NSTimer 这样的东西),支持自定义获取器(如惰性实例化)等。混合访问器和实例变量极易出错。很容易忘记需要以哪种方式访问哪个变量。一致性是良好 ObjC 的标志。

如果您绝对必须声明某个实例变量,那么应该像 JeremyP 所说的那样在 @implementation 块中进行声明。


更新(Oct-2013):

Apple 指导方针(来自 Programming with Objective-C: Encapsulating Data):

大多数属性由实例变量支持

通常,即使您从其自身的实现中访问对象的属性,也应该使用访问器方法或点语法进行属性访问,在这种情况下,应该使用 self

...

这个规则的例外是在编写初始化、释放或自定义访问器方法时,如本节后面所述。


KVO对于仅限内部使用的内容并不重要,子类化也是如此。一个类的实现部分不应该泄漏出任何东西,即使是给子类。这是良好面向对象编程的标志。 - JeremyP
1
尽管有KVO(键值观察)存在(虽然偶尔会使用自我观察),但由于您经常需要访问私有属性的访问器(惰性实例化、线程安全等),而且使用它们是简单和便宜的,除非有强烈的理由不这样做(即dealloc),否则应始终使用它们。有时使用ivars,有时使用访问器容易出错,并且难以审核其正确性。 - Rob Napier
如何在没有实例变量的情况下进行惰性实例化?我再重复一遍:根据苹果列表上报告的官方说法,如果您使用ARC,则应使用实例变量来存储内部状态,但如果您使用传统的引用计数,则应该使用属性来代替。 - JeremyP
@JeremyP,只是好奇,关于这个苹果文档在哪里? - Alex311
@Alex311 我不认为我曾经看到过来自 Apple 的任何官方文件。我提到的文档是一封由苹果员工发送到 Objective-C 列表的电子邮件。 - JeremyP
苹果官方推荐的链接在答案中(更新于2013年10月)。 - Rob Napier

0

这个问题之前在这里有提到过

当你使用synthesize时,实例变量会被处理并为您实例化。如果您正在使用新版本的XCode的Lion,则还请查看ARC中的各种属性在过渡到ARC中


1
我认为这不是同一个问题,但我更新了我的问题,希望能更清楚地表达。 - Besi

0

你总是可以从外部访问属性。因此,如果你想让一个变量只能在类内部被读取,你仍然需要声明一个 iVar。此外,使用 object->ivar 访问公共 iVar 比使用方法调用略快。


2
这不是真的。如果您在.m文件中声明属性会怎样呢? - Martin
1
@Martin:由于属性是作为一对方法实现的,因此无论在何处声明,它始终在运行时可用。 - JeremyP
@Martin:你不知道的是哪一部分呢?是属性实际上是一个设置器和获取器方法,还是方法总是有效地公共的? - JeremyP
@JeremyP 不知道它们一直都是公开的。 - Martin
1
直接使用 -> 获取内部 ivars 虽然比使用 setter 更快,但几乎从来不是正确的答案。当你进行这种优化时,几乎总会有更好的优化方法可以代替。这是一种非常危险的技术,不适合普遍使用。 - Rob Napier
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接