属性及其支持ivars

时间:2013-07-27 12:03:37

标签: ios objective-c

您可以想象我在.h文件中有属性:

@property (nonatomic) NSString * myText;
@property (nonatomic) SomeClass * someObj;

现在,在类实现中。

说,我没有忘记使用合成,我打电话给:

@synthesize myText, someObj;

现在在代码中说我忘记将self放在属性名称之前(并直接引用ivar):

myText = @"Hello";
someObj = [[SomeClass alloc] init];

我的问题是:这是一个问题吗?它会导致什么问题?或者没什么大不了的?

PS。说我正在使用ARC。

5 个答案:

答案 0 :(得分:2)

  

我的问题是:这是一个问题吗?

这称为“直接ivar访问”。在某些情况下,这不是问题,而是必需品。初始化器,dealloc和访问器(setter / getters)是您应该直接访问self的ivars的地方。在几乎所有其他情况下,您都会赞成访问者。

应避免直接访问self以外的实例的ivars。这里容易出问题的是,您可能会读取或写入无效的地址(未定义的行为),就像C结构一样。当消息对象为nil时,不执行该消息的实现。

  

会导致什么问题?

最大的两个:

  • 您不会收到有关这些更改的KVO通知
  • 您通常会绕过提供正确语义的实现(可以证明是合理的)。在这种情况下,语义可以等同于存储器管理,复制,同步或状态改变的其他后果。例如,如果重写了一个setter,那么你绕过该setter的任何子类覆盖,这可能会使该对象处于不一致状态。

另请参阅:Why would you use an ivar?

答案 1 :(得分:1)

为清楚起见,我建议始终使用

self.propertyname

而不是

propertyname

因为这消除了什么变量属于该类或在方法中已在本地声明的任何混淆。

要强制执行此操作,请尽量避免使用@synthesize,只有在您提供两个自定义getter setter(但不是一个或另一个)时才需要

编译器自动允许你在getter / setter中使用_propertyname(这是防止函数递归调用所必需的)

答案 2 :(得分:1)

只有在您打算这样做时,才应该意外访问基础实例变量。

意外的副作用可能是KVO不起作用,不会调用覆盖访问器方法,copyatomic属性也没有效果。

您不需要使用@synthesize since Xcode 4.4,如果您使用默认综合,则编译器会执行等效的

@synthesize myText = _myText;

这样

_myText = @"Hello";
self->_myText = @"Hello";

是等效的,myText = @"Hello";会导致“未定义的标识符”编译器错误。

如果编译器只使用@synthesize myText(出于向后兼容性原因):

@synthesize myText = myText;

容易出错。

请注意,有valid reasons来使用底层实例变量而不是访问者 - 但是这样做的风格很糟糕。

答案 3 :(得分:0)

30年来,推荐的做法是:

  • 使用getter / setter方法或新的.运算符来读写ivars。
  • 只在必要时才直接访问ivars。
  • 选择ivar名称以防止意外使用它们,除非ivar是永远可以直接访问的名称(这就是为什么默认行为和惯例是使用下划线为ivars加前缀)。

您需要在以下几种情况下直接访问ivars:

  • 手动内存管理需要它。如果启用ARC,则不需要此项。
  • 如果您要快速连续读取变量变量数百万次,并且由于某种原因无法将其分配给临时变量。
  • 当您使用低级C API时,可能需要指向ivar的指针,例如,Apples libxml2 sample code直接访问ivars。
  • 当您自己编写getter或setter方法时,而不是使用默认的@synthesize实现。我个人一直这样做。

除了这些情况(以及其他一些情况),请不要直接访问ivars。并在所有ivars前加下划线,以确保您不会意外访问它们并防止它们在您编码时出现在xcode的自动完成/智能感知中。

该公约的两个主要原因是:

  • 当类的底层内存结构发生更改时,可以保留Getter / setter方法和属性。如果你重命名一个ivar,所有读取ivar的代码都会中断,所以最好没有代码或几乎没有直接访问ivars的代码。
  • 子类可以覆盖getter和setter。他们不能覆盖ivars。有些人认为不应该允许子类覆盖getter和setter - 这些人都错了。能够覆盖事物是创建子类的整点
  • 如果直接访问ivars,KVC和KVO等基本功能可能会崩溃。

当然,你可以做任何你想做的事。但是这个会议现在已经存在了几十年,而且很有效。没有理由不遵循它。

答案 4 :(得分:-1)

与其他答案似乎达成一致意见相反,我建议始终使用直接ivar访问,除非您非常清楚自己在做什么。

我的推理很简单:

  1. 使用ARC,使用直接属性访问并不复杂,只需指定一个 对ivar和ARC的价值在于内存管理。

  2. (这是我的要点:)属性访问者可能有副作用 这不仅适用于您编写的属性访问器,也可能适用于 您正在实施的类的子类 现在,在子类中定义的这些访问器可能很好地依赖于子类的状态 设置在它的初始化程序中,此时尚未执行,因此您调用它们 访问器可能会导致从对象的未定义状态到应用程序的任何内容 抛出异常并崩溃。

  3. 现在,并非每个类都可以设计为子类,但我认为最好只使用一种风格,而不是根据您当前正在编写的类不一致。

    旁注:我还建议在每个ivar的名称前加上_,因为当你没有@synthesize时,编译器会自动为你的属性做。