最佳实践:如果返回类型为NSArray,则返回mutableArray.copy或mutableArray

时间:2014-12-09 21:56:27

标签: objective-c

- (NSArray *)map:(id (^)(id))block {
    NSEnumerator * enumerator = self.objectEnumerator;
    NSMutableArray * array    = [NSMutableArray array];
    id obj;
    while ((obj = enumerator.nextObject)) {
        [array addObject:(block(obj) ?: [NSNull null])];
    }
    return array (NSMutableArray *); or return array.copy (NSArray *)
}

这是 NSArray

上的类别

5 个答案:

答案 0 :(得分:4)

来自Receiving Mutable Objects

  

使用返回类型,而不是自省

     

要确定它是否可以更改接收的对象,消息的接收者必须依赖于返回值的正式类型。例如,如果它接收到一个类型为immutable的数组对象,它就不应该尝试改变它。根据类成员资格确定对象是否可变是不可接受的编程习惯。

     

[...]

     

您不应该基于类成员资格来假设对象是否可变。您的决定应该完全取决于出售该对象的方法的签名对其可变性的描述。如果您不确定对象是可变的还是不可变的,则假设它是不可变的。

一般来说:

  • 如果您的来电者遵循这些指导原则,只需返回可变数组,因为它稍微便宜一点,并且来电者不会改变它。

  • 如果您不确定来电者是否可能不遵守这些指南,并且您想要采取防御性措施,请使用array.copy返回不可变副本。

具体到这种情况,它并不重要,因为即使调用者改变它,你也不会再次使用该数组实例。

答案 1 :(得分:3)

在给定的代码中,创建可变数组只是为了返回。它不是由其他任何东西持续进行的。因此,直接归还是没有危险的。

返回一个可变数组有两个主要危险,它继续由返回代码保存:

  • 拥有代码会在调用者持有引用时对其进行变更。当调用者不期望它时,数组会发生变化。
  • 调用者改变了数组(即使它不应该因为它作为不可变类型返回而且调用者应该尊重它)。拥有代码的数组已从其下方更改。

因此,如果该方法返回一个可变数组,它继续保持对引用的引用,那么它应该返回一个副本。

答案 2 :(得分:3)

在这种情况下,您需要复制。

数组对象是在本地创建的,因此无法通过其他方式对其进行意外修改。

接口只指定它返回NSArray *,因此只要它返回一种NSArray *,它就满足了要求。它是返回MyCustomArray *还是NSMutableArray *纯粹是实现细节,它在调用者点视图中不会产生任何影响。

但是,如果共享数组对象,则执行需要返回它的副本。否则NSArray上的不可变假设可能会被破坏并导致意外行为。

答案 3 :(得分:0)

这取决于您返回的方法类型。如果你的返回类型是不可变的,请使用copy或者mutableCopy。

答案 4 :(得分:0)

在大多数情况下,无需创建副本,因为您没有在方法之外另外引用array

然而,要考虑的一件事是,在引擎盖下,这些对象是不同的。如果您在调试器中打印对象,您会发现其中一个__NSArrayM而另一个__NSArrayI

编译器会通过不让您直接在返回的NSArray上调用突变消息来保护您,但这并不能阻止您简单地将__NSArrayM转换为NSMutableArray或执行对象上的突变选择器并绕过编译器。在__NSArrayI上调用突发消息会导致应用程序崩溃,因此如果您想要保证此安全性,请在返回时调用copy即可。