可以说我有一个简单的类,如下所示:

@interface A {
// @public
    int var;
}
// @property(some_property) int var;
@end

当我想访问变量var时,我有一些选择。如果我将var公开,则可以执行以下操作:
A objectA = [ [ A alloc ] init ];
NSLog( @"%d", objectA->var );
objectA->var = someNumber;

如果改为将其设置为属性,则必须执行以下操作:
A objectA = [ [ A alloc ] init ];
NSLog( @"%d", objectA.var ); // dot-syntax
NSLog( @"%d", [ objectA var ] ); // get-syntax
[ objectA setVar: someNumber ];

我都尝试过,并且它们都可以正常工作,但是我的问题是,使用旧式指针表示法访问对象内部的变量有多危险?以后我是否需要担心通过标准化方法访问现在应该处理的事情?还是我可以随心所欲地做到这一点,只要它能起作用就行吗?

最佳答案

自从几天前我对另一个问题的评论开始类似的讨论以来,我将投入自己的2美分。

您应该始终使用访问器方法在该对象的类的实现之外设置和获取该对象的属性。您几乎应该始终使用访问器方法来访问类的属性,即使在所述类的实现内部也是如此。

以下列出了一些原因:

  • Encapsulation类可以将一个属性公开给外部世界,就像其他任何属性一样,但是在内部它不受相应的ivar支持。也许实际上是对另一个内部属性进行了某种转换。更重要的是,此实现可能会随时更改。封装在OO代码中的主要原因之一是,可以更改类的内部实现,而无需该类的外部用户进行更改。 (今天早上,我在一个重要的老类中进行了这样的更改-从ivar到完全不同的支持方法。如果一大堆其他类,甚至同一类中的方法都在直接进行ivar访问, ,我将不得不更改比我更多的代码。)
  • 内存管理。 这与ARC没什么大不了的,因为可以通过两种方式(大多数情况下,请参见下文)正确地对其进行处理,但是通过手动内存管理,属性的访问器方法将负责适本地保留和释放底层内容。目的。将这些代码保存在一个地方可以大大减少内存管理错误(泄漏和过大释放)的机会,使其更易于阅读,更易于修改,代码更少冗长。即使启用了ARC,使用复制行为声明的属性仍依赖于setter方法来执行复制,如果您直接进行ivar访问,则可以绕开该属性。
  • 设置时的自定义行为。 这实际上只是封装的另一部分。除了在调用相应的setter时将ivar设置为新值之外,类还想做一些其他事情是很常见的。一个非常常见的简单示例是,当影响其外观的属性发生更改时,NSView子类将调用[self setNeedsDisplay]。如果不调用setter,而是直接设置ivar,则将完全绕开此行为。再次,您可能会认为很好,只要您知道 setter 不需要执行此类操作即可,但是需求会发生变化,并且通过从一开始就使用 setter ,您可以更轻松地进行线下更改。
  • 在get/lazy instantiation上的自定义行为。 的最常见原因之一是执行惰性实例化。您为属性实现getter方法,以便它检查基础ivar是否为nil,如果是,则在返回ivar之前先对其进行初始化。下次调用它时,将设置ivar,因此不会再次发生初始化。这是将昂贵的(CPU密集型和/或内存密集型)对象的创建推迟到实际需要之前的一种简便方法。例如,通常将其作为性能优化来缩短启动时间,这是封装的完美示例,可以在不破坏外部代码对类的现有使用的情况下,对类的实现进行简单的改进。
  • KVO合规性。 Objective-C提供了一种称为“键值观察”的机制,该机制允许在任何时候更改另一个对象的给定属性时,一个对象可以请求通知。如果使用properly named访问器或合成的@properties,则会自动获得对KVO的支持。但是,要使KVO起作用,必须实际调用访问器方法。如果直接更改ivar,则不会通知该ivar对应属性的观察者。无论您是设置另一个对象的属性还是自身的属性,您都不知道是否为该属性的更改注册了观察者,并且正在使他们收到更改通知。
  • 可读性。 这并不完全适用于您的objectA->var示例,它更适用于类的自身实现(var = ...)中的直接ivar访问,其中self->由编译器隐含/插入。问题是,尤其是在长方法中,您可能会看到一个赋值语句,而一眼不知道所赋值的变量是否在当前作用域或实例变量本地。可以通过使用诸如下划线,Hungarian notation等为ivars前缀等命名约定来缓解这种情况,但是,Objective-C约定仍然意味着最好使用self.var = ...[self setVar:...](顺便说一句,它们在语义上是完全相同的)。
  • 为什么不呢? 有些不使用访问器方法,但是我想不出为什么有任何好的理由。键入不是真的快多少,因为在变量名前加上“self”即可。只是不需要太多的打字。由于您添加了额外的ObjC消息发送,因此调用访问器会导致性能下降。但是,这种损失很小,当然,过早的优化是不好的。访问器方法调用的开销足以严重影响应用程序性能是非常罕见的。

  • 请记住,关于在类的实现中使用直接ivar访问,我使用了“几乎”限定符。如果实现自定义访问器方法(而不是@synthesizing属性),则必须直接在访问器的实现内部访问ivar。另外,有些人不同意,但是直接在类的初始值设定项(-init)方法中设置ivars是一个好主意(也是Apple的建议)。这样做的原因是,当一个类没有完全初始化时,您可能希望避免setter的副作用。例如,您不希望KVO观察者在发现通知对象处于不一致/部分初始化状态时收到更改通知。

    关于objective-c - 在Objective-C中使用指针式分配与 setter 方法有多危险?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14903920/

    10-10 18:22
    查看更多