【问题标题】:Best Practice: Return mutableArray.copy or mutableArray if return type is NSArray最佳实践:如果返回类型为 NSArray,则返回 nsmutableArray.copy 或 nsmutableArray
【发布时间】:2015-02-07 23:22:02
【问题描述】:
- (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

上的一个类别

【问题讨论】:

  • 您对来电者的信任程度如何,他对您的信任程度如何。如果你和他是同一个人,你想怎么做就怎么做。如果这是“外部”API,请制作副本。

标签: objective-c


【解决方案1】:

来自Receiving Mutable Objects

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

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

[…]

您不应该根据类成员资格来假设对象是否可变。您的决定应仅以出售对象的方法的签名说明其可变性的内容为指导。如果你不确定一个对象是可变的还是不可变的,就假设它是不可变的。

一般来说:

  • 如果您的调用者遵循这些准则,只需返回可变数组,因为它稍微便宜一些,并且调用者不会改变它。

  • 如果您不确定调用者是否可能不遵循这些准则,并且您想进行防御性编码,请使用 array.copy 返回一个不可变副本。

具体到这种情况,没关系,因为即使调用者改变了它,你也永远不会再使用那个数组实例了。

【讨论】:

    【解决方案2】:

    在大多数情况下,不需要创建副本,因为您没有在方法之外持有对 array 的另一个引用。

    然而,需要考虑的一点是,在底层,这些对象是不同的。如果您在调试器中打印对象,您会发现一个是__NSArrayM 而另一个是__NSArrayI

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

    【讨论】:

      【解决方案3】:

      在这种情况下,你确实不需要复制。

      数组对象是本地创建的,所以不能被其他东西意外修改。

      接口只指定返回NSArray *,所以只要返回一种NSArray *,就满足要求。无论是返回MyCustomArray * 还是NSMutableArray * 都是纯粹的实现细节,从调用者的角度来看不会有任何影响。

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

      【讨论】:

        【解决方案4】:

        在给定的代码中,创建可变数组只是为了返回。它不是由其他任何东西持续持有的。所以直接退货是没有危险的。

        返回一个由返回代码继续持有的可变数组有两个主要危险:

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

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

        【讨论】:

          【解决方案5】:

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

          【讨论】:

          • 这不是问题。
          猜你喜欢
          • 2019-07-12
          • 2018-03-26
          • 1970-01-01
          • 1970-01-01
          • 2015-04-12
          • 1970-01-01
          • 2022-06-15
          • 1970-01-01
          • 2018-12-22
          相关资源
          最近更新 更多