2010-09-20 25 views
17

说我有一个这样的类:在内部访问实例变量时,我应该使用属性还是直接引用?

@interface MyAwesomeClass : NSObject 
{ 
@private 
    NSString *thing1; 
    NSString *thing2; 
} 
@property (retain) NSString *thing1; 
@property (retain) NSString *thing2; 
@end 

@implementation MyAwesomeClass 
@synthesize thing1, thing1; 
@end 

当访问thing1thing2内部(即,MyAwesomeClass实施内),是它更好地使用该属性,或者只是直接引用实例变量(假设我们没有在“自定义”访问或增变器中做任何工作,即我们只是设置并获取变量)。 Pre-Objective C 2.0,我们通常直接访问ivars,但现在通常的编码风格/最佳实践是什么?如果一个实例变量/属性是私有的,并且根本不可访问,那么这个建议是否会改变?你是否应该为每个伊娃创建一个财产,即使它们是私人的,或者只是面向公众的数据?如果我的应用程序不使用键值编码功能(因为KVC只针对属性访问)?

我对超越低级技术细节感兴趣。例如,给定(子最佳)码等:

@interface MyAwesomeClass : NSObject 
{ 
    id myObj; 
} 
@proprety id myObj; 
@end 

@implementation MyAwesomeClass 
@synthesize myObj; 
@end 

我知道myObj = anotherObject在功能上相同self.myObj = anotherObj

但是,属性不仅仅是指导编译器为您编写访问器和修改器的花哨语法,当然,它们也是更好地封装数据的一种方式,也就是说,您可以在不重写依赖这些属性的类的情况下更改该类的内部实现。我对处理该类自己的内部代码时解决此封装问题的重要性的答案感兴趣。此外,正确编写的属性可以激发KVC通知,但直接ivar访问不会;如果我的应用程序不使用KVC功能现在,以防万一它在将来可能

回答

8

我不认为有任何方法是'更好'。你可以看到两种风格都是常用的,所以现在甚至没有通常的/最佳的做法。根据我的经验,使用的样式对我如何消化我正在查看的一些实现文件的影响很小。当你看着别人的代码时,你当然希望能够适应这两种风格(以及其中的任何一种风格)。

在维护方面,对每个内部伊娃使用财产可能会略微过分。我已经完成了它,并且增加了一笔不重要的工作,我不认为这些工作能够为我付钱。但是,如果你有强烈的愿望/强迫症看到一致的代码,如self.var无处不在,并且每当你看一堂课时你都会想到它,然后使用它。不要低估唠叨感会对生产力产生的影响。

异常 - 显然,对于自定义获得者(例如懒惰创建),您没有多少选择。另外,当它使它更方便时(例如,使用所有权语义设置对象),我创建并使用内部设置器的属性。

“以防万一”,“可能”并不是一个不需要更多数据就可以做某件事的强制性理由,因为实现它所需的时间非零。一个更好的问题可能是,某个班的所有私人ivars将来需要KVC通知的可能性是多少,但现在不是?对于我自己的大部分班级来说,答案都是非常低的,所以我现在避免了为每个私人ivar创建属性的硬性规定。

我发现在处理内部实现时,我很快就会很好地处理每个ivar应该如何被访问。

如果你有兴趣,我自己的做法是这样的:

  • 阅读的ivars:直接访问,除非有一个自定义的吸气剂(如懒创造)
  • 写作的ivars:直接在alloc/dealloc。在其他地方,通过私人财产(如果存在的话)。
3

thing1 = something;self.thing1 = something;分配唯一的区别是,如果你想拥有的属性赋值操作(retaincopy等),分配的对象上完成的,那么你需要使用属性。没有属性的分配实际上就是这样,分配一个对所提供的对象的引用。

我认为为内部数据定义属性是不必要的。只定义经常访问的ivars的属性,并且需要特定的修改器行为。

11

如果你花时间在cocoa-dev邮件列表上,你会发现这是一个非常有争议的话题。

有些人认为ivars应该只能在内部使用,并且该属性不应该(或很少)使用,除非外部使用。 KVO通知和访问器副作用存在各种问题。

有些人认为你应该总是(或主要)使用属性而不是ivars。这里的主要优点是你的内存管理很好地包含在访问器方法中,而不是遍布在你的实现逻辑中。 KVO通知和访问者副作用可以通过创建指向相同伊娃的单独属性来克服。

看着苹果的示例代码将揭示他们在这个主题上的所有地方。有些样本在内部使用属性,有些使用ivars。

总的来说,我认为这是一个品味问题,而且没有正确的方法去做。我自己混合使用了两种风格。

+0

谢谢。你和Allen Ding都有特别好的答案。 – mipadi 2010-09-30 14:29:24

2

如果thing1与KVO一起使用,则在设置时使用self.thing1=是个好主意。如果thing1@public,那么最好假定有一天某个人有时想用KVO来使用它。

如果thing1有你不想重复你到处设置复杂的语义(例如retain,或非nonatomic)然后通过self.thing1=使用是一个好主意。

如果基准测试表明,调用setThing1:正在显著时间,那么你可能要考虑如何将其设置不使用self.thing1= - 也许注意它不能被KVO'ed,或者看看是否手动执行志愿是更好(例如,如果您在某个环路中将其设置为3000次,则可以通过self->thing1 3000次进行设置,然后进行2次KVO调用,以了解将要更改并更改的值)。

如果你知道你没有使用KVO,那么就会在私有变量上留下一个简单的setter。在那个时候它不再是一个技术问题,而是属于代码风格。至少只要配置器不会在配置器中显示为瓶颈。我倾向于在当时使用直接伊娃尔访问(除非我认为我将来会将KVO看作是价值,或者可能希望公开它,并且因此认为其他人可能想要KVO)。

但是,当我设置直接ivar访问的东西时,我尝试仅通过self->thing1=来完成,这使得它更容易找到它们,并且如果我发现需要使用KVO或更改它们,或者制作更复杂的存取器。

0

这里提到的其他东西都是正确的。其他答案错过的几件事是:

首先,始终记住访问器/增变器是虚拟的影响(就像所有的Objective-C方法一样)。总的来说,有人说应该避免调用虚拟方法在init和dealloc中,因为你不知道子类会做什么会让你感到困惑。出于这个原因,我通常尝试直接在init和dealloc中访问iVar,并通过其他地方的访问器/增值器访问它们。另一方面,如果您在所有其他地方不一致地使用访问器,则覆盖它们的子类可能会受到影响。

相关地,如果您直接访问iVar,并且直接访问init & dealloc以外的任何位置,则不能为任何人维护属性的原子性保证(即您的@properties被声明为原子)。如果你需要一些原子,不要直接访问iVar来抛弃原子性。同样,如果您不需要这些担保,请将您的财产申报为非原则(用于表现)。

这也与KVO问题有关。在初始阶段,没有人可能正在观察你(合法),而在dealloc中,任何剩余的观察者都有一个陈旧的未保留(即伪造)引用。同样的推理也适用于属性的原子性保证。 (即在init返回之前发生并发访问,并且在dealloc期间发生的访问本质上是错误)。

如果混合匹配direct和accessor/mutator的使用,那么您将面临运行不仅与KVO和原子性相冲突的风险,而且subclassers以及。

相关问题