3

嗨想象我在 .h 文件中有属性:

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

现在,在类实现中。

说,我没有忘记使用synthesize,我调用了:

@synthesize myText, someObj;

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

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

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

附言。假设我正在使用 ARC。

4

5 回答 5

2

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

这称为“直接 ivar 访问”。在某些情况下,这不是问题,而是必需品。Initializers、dealloc 和访问器(setter/getter)是您应该直接访问selfivars 的地方。在几乎所有其他情况下,您都会偏爱访问器。

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

它会导致什么问题?

最大的两个:

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

另请参阅:为什么要使用 ivar?

于 2013-09-01T19:51:08.720 回答
1

您不应该意外访问底层实例变量,只有当您打算这样做时。

意想不到的副作用可能是 KVO 不起作用,覆盖访问器方法没有被调用,copyandatomic属性没有效果。

@synthesize 从 Xcode 4.4 开始您不需要使用,如果您使用默认合成,编译器会执行相当于

@synthesize myText = _myText;

以便

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

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

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

@synthesize myText = myText;

这很容易出错。

请注意,使用底层实例变量而不是访问器是有正当理由的——但意外地这样做是不好的。

于 2013-09-03T01:30:41.857 回答
1

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

self.propertyname

propertyname

因为这消除了哪些变量属于该类或已在方法的上面本地声明之间的任何混淆。

要强制执行此操作,请尽量避免使用 @synthesize,仅当您同时提供自定义 gettersetter(但不是其中一个)时才需要这样做

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

于 2013-07-27T13:38:38.463 回答
0

30 年来,推荐的做法是:

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

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

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

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

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

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

当然,你可以为所欲为。但是该公约已经存在了几十年,并且有效。没有理由不遵循它。

于 2013-09-08T09:52:16.287 回答
-1

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

我的理由很简单:

  1. 使用 ARC,使用直接属性访问并不复杂,只需为 ivar 分配一个值,ARC 负责内存管理。

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

现在,并非每个类都可以设计为子类,但我认为最好在任何地方都使用一种样式,而不是根据您当前编写的类而不一致。

附带说明:我还建议为每个 ivar 的名称添加前缀_,因为当您不使用@synthesize它们时,编译器会自动为您的属性执行此操作。

于 2013-09-05T13:46:07.577 回答