在Objective-C中使用指针式分配与设置器方法有多危险? [英] How dangerous is it to use pointer-style assignment versus setter-methods in Objective-C?

查看:84
本文介绍了在Objective-C中使用指针式分配与设置器方法有多危险?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们说一个简单的类,如下所示:

Lets say I have a simple class like the following:

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

当我想访问变量var时,我有一些选择.如果我将var公开,则可以执行以下操作:

When I want to access the variable var, I have some options. If I make var public, I can do this:

A objectA = [ [ A alloc ] init ];
NSLog( @"%d", objectA->var );
objectA->var = someNumber;

如果我改为将其设置为属性,则必须做更多类似的事情:

If I make it a property instead, I'll have to do something more like this:

A objectA = [ [ A alloc ] init ];
NSLog( @"%d", objectA.var ); // dot-syntax
NSLog( @"%d", [ objectA var ] ); // get-syntax
[ objectA setVar: someNumber ];

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

I've tried both and they work fine but my question is how dangerous is it to use the old-style pointer notation to access variables inside of an object? Will I have to worry about things later on that I should take care of now by standardizing my accessing of methods? Or can I get away with doing it however I want so long as it works?

推荐答案

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

I'll throw my own 2 cents in since a comment of mine on another question started a similar discussion a few days ago.

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

You should always use accessor methods to set and get an object's properties outside of the implementation of that object's class. You should almost always use accessor methods to access a class's properties even inside the implementation of said class.

以下是一些原因的列表:

Here is a list of some reasons why:

  • 封装.类可以将一个属性公开给外部世界,就像其他任何属性一样,但是在内部却不受相应的ivar支持.也许实际上是对另一个内部属性进行了某种转换.更重要的是,此实现可能会随时更改.封装在OO代码中的主要原因之一是,可以更改类的内部实现,而无需该类的外部用户进行更改. (今天早上,我在一个重要的老类中进行了这样的更改-从ivar到完全不同的支持方法.如果一大堆其他类,甚至同一类中的方法都在直接进行ivar访问, ,我将不得不更改比我多得多的代码.)

  • Encapsulation. A class may expose a property that to the outside world looks just like any other property, but internally is not backed by a corresponding ivar. Perhaps it actually does some transformation of another, internal property. More importantly, this implementation may change sometime down the line. One of the major reasons for encapsulation in OO code is that the internal implementation of a class can be changed without requiring external users of that class to make changes. (I made just such a change -- from an ivar to a completely different backing method -- in an important, and old class just this morning. If a bunch of other classes, or even methods in the same class were doing direct ivar access, I would have had to change a lot more code than I did.)

内存管理.与ARC没什么大不了的,因为无论哪种方式都可以正确处理(大多数情况下,请参见下文),但是使用手动内存管理,属性的accessor方法将注意正确保留和释放基础对象.将这些代码保存在一个地方可以大大降低内存管理错误(泄漏和过大释放)的机会,使其更易于阅读,更易于修改,代码也不那么冗长.即使启用了ARC,使用复制行为声明的属性仍依赖于setter方法来执行复制,如果您直接进行ivar访问,则可以绕开该操作.

Memory management. This isn't as big of a deal with ARC, since it will (mostly, see below) be handled properly either way, but with manual memory management, the accessor method for a property will take care of properly retaining and releasing the underlying object. Keeping this code in one place makes for a much lower chance of memory management mistakes (leaks and overreleases), easier to read, easier to modify, less verbose code. Even with ARC enabled, properties declared using copy behavior rely on the setter method to perform the copy, and you bypass that if you do direct ivar access.

自定义行为.这实际上只是封装的另一部分.除了在调用相应的setter时将ivar设置为新值之外,类还想做一些其他事情是很常见的.一个非常常见的简单示例是,当影响其外观的属性发生更改时,NSView子类将调用[self setNeedsDisplay].如果不调用setter,而是直接设置ivar,则将完全绕开此行为.再次,您可能会认为很好,只要您知道设置器不需要执行此操作,但是需求会发生变化,并且通过从一开始就使用设置器,您可以更轻松地进行更改.

Custom behavior on set. This is really just another part of encapsulation. It's very common for a class to want to do something beyond just setting an ivar to a new value when the corresponding setter is called. One very common, simple example is for an NSView subclass to call [self setNeedsDisplay] when a property that affects its appearance is changed. If you don't call the setter, instead setting the ivar directly, you will bypass this behavior altogether. Again, you might think it's fine as long as you know that the setter doesn't need to do such a thing, but requirements change, and by using a setter from the start, you make a change down the line much easier.

get/惰性实例化.这样做的最常见原因之一就是进行了惰性实例化.您为属性实现getter方法,以便它检查基础ivar是否为nil,如果是,则在返回ivar之前先对其进行初始化.下次调用它时,将设置ivar,因此不会再次发生初始化.这是将昂贵的(CPU密集型和/或内存密集型)对象的创建推迟到实际需要之前的一种简便方法.例如,通常将其作为性能优化来缩短启动时间,这是封装的完美示例,可以在不破坏外部代码对类的现有使用的情况下,对类的实现进行简单的改进.

Custom behavior on get/lazy instantiation. One of the most common reasons for this is to do lazy instantiation. You implement the getter method for a property so that it checks to see if the underlying ivar is nil, and if it is, it first initializes the ivar before returning it. The next time it's called, the ivar will be set so the initialization won't happen again. This is an easy way to defer the creation of expensive (CPU intensive and/or memory intensive) objects until and if they are actually needed. For example, it's often done as a performance optimization to improve launch times, which is a perfect example of encapsulation allowing simple improvements to a class's implementation without breaking external code's existing use of a class.

KVO 合规性. Objective-C提供了一种称为键值观察"的机制,该机制允许在任何时候更改另一个对象的给定属性时,一个对象就可以请求通知.如果您使用正确命名访问器或合成的@properties,您将自动获得对KVO的支持.但是,要使KVO起作用,必须实际调用访问器方法.如果直接更改一个ivar,则将不通知该ivar对应属性的观察者.无论您是设置另一个对象的属性还是自身的属性,您都不知道是否为该属性的更改注册了观察者,并且使您的更改通知被短路.

KVO compliance. Objective-C provides a mechanism called Key Value Observing that allows one object to request a notification any time a given property of another object is changed. If you use properly named accessors, or synthesized @properties, you get support for KVO automatically. However, for KVO to work, the accessor methods have to actually be called. If you change an ivar directly, observers of that ivar's corresponding property will not be notified. Whether you're setting another object's property or a property on self, you don't know if observers are registered for changes on that property and you're short circuiting their being notified of the change.

可读性.这并不完全适用于您的objectA->var示例,它更适用于类自身实现(var = ...)中的直接ivar访问,其中self->由编译器隐含/插入.问题是,尤其是在长方法中,您可能会看到一个赋值语句,而一眼不知道所赋值的变量是否在当前作用域或实例变量本地.可以使用诸如下划线为ivars前缀等命名约定来缓解这种情况,匈牙利表示法,等等,但是仍然使用Objective-C约定意味着最好使用self.var = ...[self setVar:...](顺便说一句,它们在语义上完全相同).

Readability. This doesn't exactly apply to your objectA->var example, it's more applicable to direct ivar access in a class's own implementation (var = ...), where self-> is implied/inserted by the compiler. The problem is that, particularly in a long method, you may see an assignment statement, and not know at glance if the variable being assigned is local to the current scope, or an instance variable. This can be alleviated with naming conventions like prefixing ivars with an underscore, Hungarian notation, etc., but still Objective-C conventions mean that it's best to use self.var = ... or [self setVar:...] (which are exactly the same semantically, by the way).

为什么不呢?有些人不使用访问器方法,但是我想不出为什么有任何好的理由.键入不是真的快多少,因为在变量名前加上"self"即可.只是不需要太多的打字.由于您添加了额外的ObjC消息发送,因此调用访问器会导致性能下降.但是,这种损失很小,当然,过早的优化是不好的. 非常很少,访问器方法调用的开销足以严重影响应用程序的性能.

Why not? There are those that don't use accessor methods, but I can't think of any good reasons why. It's not really much faster to type, as prefixing a variable name with 'self.' just doesn't take that much typing. There's a performance penalty involved in calling an accessor since you add an extra ObjC message send. However, this penalty is very small, and of course, premature optimization is a bad thing. It's very rare that the overhead of an accessor method call is enough to seriously impact application performance.

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

Remember that I used an 'almost' qualifier regarding using direct ivar access inside a class's implementation. If you implement custom accessor methods (as opposed to @synthesizing a property), you will have to directly access the ivar inside the accessors' implementations, of course. Also, there are some who disagree, but it's a good idea (and Apple's recommendation) to directly set ivars inside a class's initializer (-init) method(s). The reason for this is that you may want to avoid setter side effects while a class is not completely initialized. For example, you don't want KVO observers to be notified of a change when they may find the notifying object in an inconsistent/partially initialized state.

这篇关于在Objective-C中使用指针式分配与设置器方法有多危险?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆