【问题标题】:How do copy and mutableCopy apply to NSArray and NSMutableArray?copy 和 mutableCopy 如何应用于 NSArray 和 NSMutableArray?
【发布时间】:2011-01-01 09:51:35
【问题描述】:

NSArrayNSMutableArray 上使用copymutableCopy 有什么区别?

这是我的理解;对吗?

// ** NSArray **
NSArray *myArray_imu = [NSArray  arrayWithObjects:@"abc", @"def", nil];

// No copy, increments retain count, result is immutable
NSArray *myArray_imuCopy = [myArray_imu copy];

// Copys object, result is mutable 
NSArray *myArray_imuMuta = [myArray_imu mutableCopy];

// Both must be released later

// ** NSMutableArray **
NSMutableArray *myArray_mut = [NSMutableArray arrayWithObjects:@"A", @"B", nil];

// Copys object, result is immutable
NSMutableArray *myArray_mutCopy = [myArray_mut copy];

// Copys object, result is mutable
NSMutableArray *myArray_mutMuta = [myArray_mut mutableCopy];

// Both must be released later

【问题讨论】:

  • 您的编辑有误;不知道是笔误还是误会。在第一个代码块中,从 mutableCopy 分配给的变量 myArray_imuMuta 是 mutable,而不像您的注释所示的那样是不可变的。
  • 谢谢,这是一个错误,我对 Xcode 感到困惑,说“警告 NSArray 可能无法响应添加 -addObject / -removeObjectAtIndex。我会将 myArray_imuMuta 更改为 NSMutableArray。
  • @JamesHarnett - 请停止以“ARC 兼容性”为名的编辑 - 请参阅 this meta post 了解原因。

标签: cocoa cocoa-touch nsmutablearray nsarray nscopying


【解决方案1】:

copymutableCopy在不同的协议中定义(分别为NSCopyingNSMutableCopying),NSArray两者都符合。 mutableCopy 是为NSArray 定义的(不仅仅是NSMutableArray),它允许您制作一个最初不可变数组的可变副本:

// create an immutable array
NSArray *arr = [NSArray arrayWithObjects: @"one", @"two", @"three", nil ];

// create a mutable copy, and mutate it
NSMutableArray *mut = [arr mutableCopy];
[mut removeObject: @"one"];

总结:

  • 无论原始类型如何,您都可以依赖mutableCopy 的结果是可变的。对于数组,结果应该是NSMutableArray
  • 不能依赖copy的结果是可变的! copying 和 NSMutableArray 可能返回 NSMutableArray,因为那是原始类,但 copying 任意 NSArray 实例不会。

编辑:根据 Mark Bessey 的回答重新阅读您的原始代码。当您创建阵列的副本时,当然,无论您对副本做什么,您仍然可以修改原始文件。 copymutableCopy 影响 新数组 是否可变。

编辑 2: 修正了我的(错误)假设,即 NSMutableArray -copy 将返回 NSMutableArray

【讨论】:

  • 向 NSMutableArray 发送 -copy 通常不会返回另一个可变数组。它当然可以这样做,但在通常情况下,它返回一个不可变的、恒定时间的访问数组。
  • 哎呀,你是对的。我误读了 OP 的代码,并认为他正在修改 -copy 的结果。 NSMutableArray 的 -copy 返回另一个可变数组同样有意义,所以我(错误地)假设是这种情况。
  • 我是否遗漏了什么,因为我找不到任何对 [mut remove: @"one"]; 的引用这不应该是 [mut removeObjectIdenticalTo: @"one"];我只是想为那些阅读并尝试学习的人澄清一下?
  • 糟糕,应该是 [mut removeObject: @"one"];。 removeObject 使用 -isEqual 来确定匹配,如果两个字符串具有相同的内容,则认为它们相等。 removeObjectIdenticalTo 仅删除传递的确切对象(它比较对象地址,而不是内容)。 [mut removeObjectIdenticalTo: @"one"] 创建一个新的临时 NSString 对象,该对象不在数组中,因此无法从数组中删除。
  • "复制一个 NSMutableArray 可能会返回一个 NSMutableArray" 不,它永远不会返回一个 NSMutableArray。请参阅developer.apple.com/library/mac/documentation/Cocoa/Conceptual/…“在“不可变与可变”概念适用于对象的情况下,NSCopying 会生成不可变副本,无论原始副本是否不可变。”
【解决方案2】:

我认为您一定误解了 copy 和 mutableCopy 的工作方式。在您的第一个示例中,myArray_COPY 是 myArray 的不可变副本。复制后,您可以操作原始 myArray 的内容,而不会影响 myArray_COPY 的内容。

在第二个示例中,您创建了 myArray 的可变副本,这意味着您可以修改数组的任一副本,而不会影响另一个。

如果我将第一个示例更改为尝试从 myArray_COPY 插入/删除对象,它会失败,正如您所期望的那样。


也许考虑一个典型的用例会有所帮助。通常情况下,您可能会编写一个采用NSArray * 参数的方法,并且基本上将其存储起来以供以后使用。你可以这样做:

- (void) doStuffLaterWith: (NSArray *) objects {
  myObjects=[objects retain];
}

...但是您遇到的问题是可以使用 NSMutableArray 作为参数调用该方法。创建数组的代码可能会在调用 doStuffLaterWith: 方法和您以后需要使用该值之间对其进行操作。在多线程应用程序中,数组的内容甚至可以在迭代时更改,这可能会导致一些有趣的错误。

如果您改为这样做:

- (void) doStuffLaterWith: (NSArray *) objects {
  myObjects=[objects copy];
}

..然后副本会在调用方法时创建数组内容的快照。

【讨论】:

    【解决方案3】:

    “copy”方法返回通过实现 NSCopying 协议 copyWithZone 创建的对象:

    如果你向 NSString 发送一条复制消息:

    NSString* myString;
    
    NSString* newString = [myString copy];
    

    返回值将是一个 NSString(不可变)


    mutableCopy 方法返回通过实现 NSMutableCopying 协议的 mutableCopyWithZone 创建的对象:

    通过发送:

    NSString* myString;
    
    NSMutableString* newString = [myString mutableCopy];
    

    返回值是可变的。


    在所有情况下,对象都必须实现协议,表示它将创建新的副本对象并将其返回给您。


    在 NSArray 的情况下,浅拷贝和深拷贝的复杂程度更高。

    NSArray 的浅拷贝只会复制对原始数组对象的引用并将它们放入新数组中。

    结果是:

    NSArray* myArray;
    
    NSMutableArray* anotherArray = [myArray mutableCopy];
    
    [[anotherArray objectAtIndex:0] doSomething];
    

    也会影响原始数组中索引 0 处的对象。


    深拷贝实际上会复制数组中包含的各个对象。这是通过向每个单独的对象发送“copyWithZone:”消息来完成的。

    NSArray* myArray;
    
    NSMutableArray* anotherArray = [[NSMutableArray alloc] initWithArray:myArray
                                                           copyItems:YES];
    

    编辑以消除我对可变对象复制的错误假设

    【讨论】:

      【解决方案4】:
      NSMutableArray* anotherArray = [[NSMutableArray alloc] initWithArray:oldArray
                                                                 copyItems:YES];
      

      将创建anotherArray,它是oldArray 的副本,深度为2 级。如果oldArray 的对象是一个数组。大多数应用程序通常都是这种情况。

      如果我们需要 True Deep Copy 我们可以使用,

      NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
          [NSKeyedArchiver archivedDataWithRootObject: oldArray]];
      

      这将确保实际复制所有级别,同时保留每个级别的原始对象的可变性。

      罗伯特·克拉伦斯·达尔梅达​​, 印度班加罗尔。

      【讨论】:

      • 你的“真正深拷贝”的概念真的很好..正是我想要的......非常感谢你+1......
      【解决方案5】:

      您在原始数组上调用 addObject 和 removeObjectAtIndex,而不是您创建的新副本。调用 copy 与 mutableCopy 只会影响对象的新副本的可变性,而不影响原始对象的可变性。

      【讨论】:

        【解决方案6】:

        简单来说,

        • copy 返回数组的不可变(不能修改)副本,
        • mutableCopy 返回数组的可变(可修改)副本。

        复制(在这两种情况下)意味着您获得了一个新数组,其中“填充”了对原始数组的对象引用(即,在副本中引用了相同的(原始)对象。

        如果您将新对象添加到 mutableCopy,那么它们对于 mutableCopy 是唯一的。如果从 mutableCopy 中删除对象,它们将从原始数组中删除。

        将这两种情况下的副本视为创建副本时原始数组的时间快照。

        【讨论】:

          【解决方案7】:

          假设

          NSArray *A = xxx; // A with three NSDictionary objects
          NSMutableArray *B = [A mutableCopy]; 
          

          B的内容是NSDictionary对象而不是NSMutableDictionary,对吗?

          【讨论】:

            【解决方案8】:
            -(id)copy always returns a immutable one & -(id)mutableCopy always returns a mutable object,that's it.
            

            您必须知道这些复制内容的返回类型,并且在声明将分配给哪个新对象时,返回值必须是不可变或可变的,否则编译器会显示错误。

            已经复制的对象不能用新的对象修改,它们现在完全是两个不同的对象了。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-02-29
              • 2010-11-23
              相关资源
              最近更新 更多