返回可变对象与返回不可变(非成员)对象

8

我几乎从未看到第二种方法被使用,我想知道为什么?

  • 它不会破坏对期望NSArray的支持(因为它是其子类)。
  • 它也不会通过揭示可变内部来破坏封装性。

在一个不返回可变实例变量的前提下,(这应该是常识)
我现在只能想到使用第二种方法的好处:

  • 它实际上是可变的。在这里改变是安全的,那么为什么要阻止呢
  • 不需要调用 [[[foo fooBar] mutableCopy] autorelease],这样不必要地分配额外的内存浪费时间

以下是不同方法的变化:

- (NSArray *)fooBar {
    NSMutableArray *fooArray = [NSMutableArray array];
    //populate fooArray
    return fooArray;
}

- (NSMutableArray *)fooBar {
    NSMutableArray *fooArray = [NSMutableArray array];
    //populate fooArray
    return fooArray;
}

我正在寻求帮助,因为我有一堆具有相同模式的方法需要处理。而且大多数情况下返回的数组之后都会被修改(合并、编辑等)。因此,我认为返回NSMutableArrays应该完全没问题,但是似乎没有人这样做。
NSMutableArray、NSMutableSet、NSMutableDictionary...基本上都是一样的。

1
加粗字体很容易就能实现。但是过多的加粗会让人感到嘈杂,适量的加粗效果更佳。 - Jeremy W. Sherman
3个回答

4

如果需要解释可变和不可变使用的原因,请查看苹果关于对象可变性的文档。

一般来说,最好返回一个不可变版本,除非你特别希望返回的对象始终是可变的,并且任何客户端都可以更改它。您应该根据接口的意图创建接口,而不是基于当前实现。可能会有要求发生变化,您需要更改fooBar的实现,使其返回一个实例变量。通过返回可变数组,您可以确保封装自己的实例变量和当前实现。

因此,您可能有一个有效的地方可以返回可变数组(我不知道),但您会看到大多数代码传递不可变数组,因为它完全封装了它们的变量和实现。


虽然你们两个的回答都很有道理,但我仍然不确定我的情况是否可能是一个罕见的情况,使用NSMutableSet可能是合适的。如果性能和内存对这个项目来说不是非常重要,我就不会费心去创建额外的可变副本(已经是可变的,但已公开的)NSSets了。我的项目可能会在这些集合中拥有数以万计(甚至更多)的对象。克隆这些需要额外的时间和内存。(也请参见我对octy答案的评论) - mtree
你的情况可能是适合这种方式的,我不确定。只要你认识到通过暴露可变状态,你强制任何实现都必须以可变状态为可接受的方式工作,并且接受你可以将其设置为可变状态。 - David V

2
我认为第一种变体更受欢迎,因为多态性更受欢迎。
无论哪种情况,两种方法都返回NSMutableArray的实例,唯一的区别在于第一种方法隐藏了这个事实。换句话说,第一种变体并不比第二种更安全。它基本上使用多态性告诉调用者可能返回任何类型的NSArray。如果您需要在代码中具有这种灵活性,它确实具有优势。(例如,如果有一天出于某种原因,您需要返回自定义的NSArray子类,您的代码将不会在那个级别上崩溃)。
然而,您似乎更喜欢向调用者传达意图 - 即您实际上返回可变数组 - 这也是可以的。为了让每个人都满意(如果真有这样的事...),我建议将第二个方法重命名为:
- (NSMutableArray *)mutableFooBar {
    NSMutableArray *fooArray = [NSMutableArray array];
    //populate fooArray
    return fooArray;
}

作为补充说明,我认为以下方法是将现有不可变数组转换为可变数组的稍微更有效率的方法:
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:fooArray];

如果我对这个假设有误,请纠正我。

希望这能回答你的问题...


仅在一个类(或者说是协议及其实现)中就已经有19个返回NSSets的方法。另外一个协议/类中有5个,还有一个有7个。如果这些方法数量翻倍,我的项目将难以维护。这些方法被设置为高度粒度化/模块化。正如我在问题中所述,它们的结果通常是联合/减去/交集。它们永远不会返回一个恰好是ivar的NSSet。相反,它们返回的是按需整理和在方法运行期间生成的对象集合。其中90%内部使用NSMutableSet。 - mtree
忘了提到我提出问题的最初原因是性能和内存至关重要。(请参阅我对David V答案的更详细评论) - mtree

0

让一个方法返回可变实例看起来很可疑。

作为调用者,您必须质疑原始方法签名,并想知道是否真的安全地改变返回值。毕竟,该类可能会无意中返回指向内部状态的指针。

如果分析表明这个副本确实很昂贵,我通常会更改方法签名,以使可变性明显。也许可以使用以下内容:

- (void)populateFooBars:(NSMutableArray *)array;

这样就清楚了结果的可变性是有意的。


我已经过度记录了我的代码(尤其是对于那些可变返回值)。我的所有类的方法都不返回(也永远不会返回)表示内部状态的NSMutableSet。这是一种内部框架约定。所涉及的方法主要返回整理的Core Data关系。仅一个类就有19个这样的方法。此外,拥有19个以“-(void)populate…”开头的方法将使阅读代码变得相当困难。还需要在每次调用时至少添加一行数组指针。(见其他答案的评论) - mtree

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接