在 NSMutableArray 的副本上使用快速枚举来删除对象的详细信息

Details of using fast enumeration on a copy of an NSMutableArray to remove objects

当你循环遍历其中的对象时,我了解到你不能从 NSMutableArray 中删除对象。

循环 [< array_object > copy] 而不是 < array_object > 修复了它。

但是,我有一些未解决的问题希望 Objective-C 专家提供意见。

  1. 在第一个 for 循环中,我希望每个 nextObject 指向不同的内存(即我认为 msgDetail 数组将有一个指针列表,每个指向特定数组索引包含的 NSDictionary 的地址)。但是所有 %p nextObject 打印都给出了相同的值。这是为什么?

    for(NSDictionary *nextObject in msgDetailArray)
    {
    
        NSLog(@"Address = %p, value = %@",&nextObject,nextObject) ;
        //Doing [msgDetailArray removeObject:nextObject] here based on some condition fails
    }
    
  2. 在第二个for循环中,nextObject NSDictionary的地址与第一个for循环中打印的地址不同

    for(id nextObject in [msgDetailArray copy])
    {
    
        NSLog(@"In copy Address = %p, value = %@",&nextObject,nextObject) ;
    
        //Doing [msgDetailArray removeObject:nextObject] here based on some condition succeeds and it also removes it from the original array even though I am looping through a copy
    }
    
  3. 但是,当我循环遍历 [msgDetailArray copy],然后执行 removeObject: 时,它会将其从原始 msgDetailArray 中删除。 removeObject: 是怎么做到的?它真的使用字典的内容并删除与内容匹配的对象吗?我认为它所做的只是检查是否有一个对象位于同一内存位置并将其删除(基于 2,我假设包含 [msgDetailArray copy] 中的字典的内存地址与中的地址不同原来的msgDetailArray)。如果它实际上是在使用内容,我将不得不非常小心,以防出现重复条目​​。

    for(id nextObject in msgDetailArray)
    {
            NSLog(@"Address = %p, value = %@",&nextObject,nextObject) ;
            //test what is left in msgDetailArray.  I see that doing removeObject on [msgDetailArray copy] does remove it from the original too.  How is removeObject working (is it actually contents of dictionary)
    }
    

复制数组会造成 "shallow copy"。它创建一个新的数组对象,然后将指向对象的指针存储在第一个数组中。它不会创建新对象。

将数组想象成地址簿。它列出了您朋友家的地址。如果我复印了你的地址簿,那么我就有了一份地址列表。通讯录不包含房屋。

如果我从我的地址簿副本中删除一个条目,它不会从您的地址簿中删除相同的条目。它也不会摧毁任何房屋。

在您的循环 2 代码中,您正在遍历副本,然后告诉您的原始数组删除某些对象。它们从原始数组中删除,但不会从副本中删除。 (这很好,因为正如您所说,在遍历数组时改变数组会导致崩溃。)

请注意,数组可以包含多个指向同一对象的指针,就像地址可以多次包含同一地址一样。 (如果一个朋友在结婚时改了名字,你可以把她写到你的地址下,写在他婚后的名字下,同时留下她娘家姓下的条目。两个地址都指向同一所房子。)

请注意,您正在使用的 removeObject 方法会删除对象的 ALL 个条目。这就像你在说 "erase every entry in my address book for 123 Elm street"。如果您只想删除重复集中的一个条目,那么您需要改用 removeObjectAtIndex,并且您需要更改代码才能使其生效。

  1. 复制操作复制容器,而不是其内容。您有一个指向相同对象的新指针列表。

  2. &nextObject是变量的地址,不是对象。对象的地址就是变量的值:NSLog(@"%p", nextObject);

  3. removeObject: 使用 isEqual: 将其参数与集合的内容进行比较,这通常不通过地址进行比较,而是通过 class.

即使不考虑快速枚举循环中的变异禁令,您也无法从副本中删除对象,因为 copy 使 不可变 复制。您需要 mutableCopy 才能更改新实例。

But all of the %ps prints are giving the same value. Why is that?

因为&nextObjectnextObject变量的地址,你的代码示例中使用的循环变量;它的类型是指向指针的指针。 nextObject 本身也是一个指针,所以如果你想打印对象的地址,你需要做的就是将它转换为 void*:

NSLog(@"Address = %p, value = %@", (void*)nextObject, nextObject);

In the second for loop, the address of the nextObject NSDictionaries are different from that printed in the first for loop

因为那是一个不同变量的地址。

when I loop through [msgDetailArray copy], and then do a removeObject, it removes it from the original msgDetailArray.

您正在循环的副本位于一个不可见的临时对象中。它是由原始数组支持的不可变副本。

调用[msgDetailArray removeObject:nextObject]对原始数组进行操作。如果您不希望出现这种效果,请将副本存储在一个变量中,然后从中删除项目:

NSMutableArray *mutableCopy = [msgDetailArray mutableCopy];
...
[mutableCopy removeObject:nextObject];

这不会触及原始数组。但是,您不能在从中删除项目的同时迭代 mutableCopy