【问题标题】:Is there any reason not to return a mutable object where one is not expected?是否有任何理由不返回不期望的可变对象?
【发布时间】:2010-10-12 23:10:46
【问题描述】:

我有许多类似于以下的功能:

+ (NSArray *)arrayOfSomething
{
    NSMutableArray *array = [NSMutableArray array];

    // Add objects to the array

    return [[array copy] autorelease];
}

我的问题是关于这个方法的最后一行:返回可变对象并避免复制操作,还是返回不可变副本更好?是否有充分的理由避免返回不期望的可变对象?

(我知道返回 NSMutableArray 是合法的,因为它是 NSArray 的子类。我的问题是这是否是个好主意。)

【问题讨论】:

  • 当然,您给出的示例可以只返回原件而不首先创建副本。另外,鉴于这是一个类方法(顺便说一下,不是函数),你真的需要每次都创建一个新的数组实例吗?如果您缓存了副本,那么这一点几乎没有实际意义。
  • +1 因为我学到了一些非常重要的东西,所有 Objective-C 程序员在研究答案时都应该知道。

标签: objective-c


【解决方案1】:

这是一个复杂的话题。我认为最好将您推荐给 Apple 在object mutability 上的指南。

关于使用自省来确定返回对象的可变性,Apple 有这样的说法:

为了确定它是否可以改变接收到的对象,接收者必须依赖于返回值的形式类型。如果它接收到,例如,一个类型为不可变的数组对象,它不应该尝试对其进行变异。根据其类成员资格确定对象是否可变是不可接受的编程实践

(我的重点)

这篇文章继续给出了几个很好的理由,说明为什么你不应该对返回的对象使用自省来确定你是否可以改变它,例如

您从文件中读取了属性列表。当 Foundation 框架处理该列表时,它会注意到属性列表的各个子集是相同的,因此它会创建一组在所有这些子集之间共享的对象。之后,您查看创建的属性列表对象并决定改变一个子集。突然间,在不知不觉中,您在多个地方更改了树。

你向 NSView 询问它的子视图(subviews 方法),它返回一个声明为 NSArray 但内部可能是 NSMutableArray 的对象。然后将该数组传递给其他代码,这些代码通过自省确定它是可变的并对其进行更改。通过改变这个数组,代码改变了 NSView 的内部数据结构。

鉴于上述情况,您在示例中返回可变数组是完全可以接受的(当然,前提是在返回它后您永远不会自己对其进行变异,因为那样您将违反合同)。

话虽如此,但几乎没有人阅读过 Cocoa Objects Guide 的该部分,因此防御性编程会要求您制作一个不可变的副本并将其返回,除非性能分析表明这样做有问题。

【讨论】:

  • 伙计,我多次提到该文档,但不知何故总是略过该部分。我正是你想要防御的那种程序员。 (在我的“辩护”中,我从未真正测试过对象的可变性。)
  • @Robot K:加入俱乐部。正如我对 kubi 的回答所附的第一条评论所显示的那样,如果我没有决定研究这个问题的主题,我永远不会质疑对返回的不可变对象使用自省的合法性。不过和你一样,我从来没有尝试过这样的事情。
  • 查看 NSObject isKindOfClass: 方法的文档以获取类似警告:developer.apple.com/library/ios/documentation/cocoa/reference/…:
【解决方案2】:

简短回答:不要这样做

长答案:这取决于。如果数组在被期望它是静态的人使用时被更改,您可能会导致一些令人费解的错误,这将是一个痛苦的追踪。最好像您所做的那样只进行复制/自动释放,并且仅在发现性能受到重大影响时才返回并重新访问该方法的返回类型。


作为对 cme​​ts 的回应,我认为返回可变数组不太可能造成任何麻烦,但是,如果它确实造成了麻烦,可能很难准确找出问题所在.如果制作可变数组的副本对性能造成很大影响,那么很容易确定导致问题的原因。您可以在两个非常不可能的问题之间进行选择,一个很容易解决,一个非常困难。

【讨论】:

  • 你能说出一个现实生活中会发生奇怪事情的案例吗?在我看来,很难在不注意的情况下弄乱数组-因为您会从编译器中收到每个“正常”方法调用的警告。当然有 performSelector 和朋友,但除此之外..?
  • 在某个地方,一些代码可以测试该类是否可变并开始修改它。对于期望不可​​变数组的其他类来说,这将是有问题的。这不太可能,但有可能发生。
  • 另外,一些类(尤其是框架类)检查它们是否传递了一个可变数组,然后复制它。返回不可变数组将跳过该步骤。
  • 当然,如果您不编写框架(或任何超出您的应用/团队外部可见的东西),那么这应该不是问题。
  • 如果您将数组传递给 Apple 或第 3 方框架,这仍然可能是个问题。此外,您必须考虑到需要维护您的代码的人(您在六个月内?)可能不知道或不记得(或可能记错)实现细节并开始做出不正确的假设。
猜你喜欢
  • 2011-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-14
  • 1970-01-01
相关资源
最近更新 更多