【问题标题】:Details of using fast enumeration on a copy of an NSMutableArray to remove objects在 NSMutableArray 的副本上使用快速枚举来删除对象的详细信息
【发布时间】:2016-03-30 07:46:39
【问题描述】:

我学到了当你循环遍历其中的对象时无法从 NSMutableArray 中删除对象的艰难方法。

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

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

  1. 在第一个 for 循环中,我希望每个 nextObjects 指向不同的内存(即我认为 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 循环中,nextObjectNSDictionarys 的地址与第一个 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)
    }
    

【问题讨论】:

    标签: ios objective-c cocoa-touch nsmutablearray fast-enumeration


    【解决方案1】:

    复制数组会执行“浅复制”。它创建一个新的数组对象,然后在第一个数组中存储指向对象的指针。它不会创建新对象。

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

    如果我从我的通讯簿副本中删除一个条目,它不会从您的通讯簿中删除相同的条目。它也不会破坏任何房屋。

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

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

    请注意,您使用的 removeObject 方法会删除对象的 ALL 条目。就像您在说“删除我地址簿中 123 Elm 街的所有条目”。如果您只想删除重复集的一个条目,则需要改用 removeObjectAtIndex,并且您需要更改代码以使其正常工作。

    【讨论】:

    • 很好的解释。只是为了确认当我调用 [ removeObject:] 时,它知道要删除哪个对象,因为它正在使用 NSMutableDictionary 的内容。那是对的吗?如果是,我假设这仅适用于某些对象,如 NSDictionary,而不适用于所有对象。是否有一些我可以查找的术语(如符合 NSCopying 协议)可以用来区分我正在处理的对象类型?
    • 所有的解释都很好,我不知道该接受哪一个!我将在明天之前接受最高票数的人!
    • removeObject: 循环遍历数组中的所有对象,并将每个对象与您使用isEqual: 方法传入的对象进行比较。对于自定义对象,您需要实现isEqual:
    • 谢谢。我做了一个实验,我创建了一个 NSMutableDictionary,其中两个元素是相同的。调用 removeObject: 删除了两个条目。因此,对于 NSMutableDictionary 来说,isEqual 使用字典元素的内容,而不仅仅依赖于匹配对象的内存。
    • 不要在多个地方发布相同的 cmets。
    【解决方案2】:
    1. 复制操作复制容器,而不是其内容。您有一个指向相同对象的新指针列表。

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

    3. removeObject: 使用isEqual: 将其参数与集合的内容进行比较,这通常不按地址进行比较,而是通过类定义的某种语义评估。

    即使不考虑在快速枚举循环中禁止更改,您也无法从副本中删除对象,因为copy 会生成一个不可变 副本。您需要mutableCopy 才能更改新实例。

    【讨论】:

    • 很好的解释。只是为了确认当我调用 [ removeObject:] 时,它知道要删除哪个对象,因为它正在使用 NSMutableDictionary 的内容。那是对的吗?如果是,我假设这仅适用于某些对象,如 NSDictionary,而不适用于所有对象(具有自定义 isEqual 的对象)。是否有一些我可以查找的术语(如符合 NSCopying 协议)可以用来区分我正在处理的对象类型?
    • 所有的解释都很好,我不知道该接受哪一个!我将在明天之前接受最高票数的人!
    • “它知道要删除哪个对象,因为它正在使用 NSMutableDictionary 的内容。正确吗?”是的,但在这种情况下,它也是完全相同的字典实例,因为正如我所说,内容没有被复制。我的回答可能不清楚。 “我假设这仅适用于 NSDictionary 等某些对象,而不适用于所有对象”我不确定你在这里的意思。许多类会覆盖isEqual:,因此有一种比“是同一块内存”更有用的方法来比较它们。
    • 谢谢。我做了一个实验,我创建了一个 NSMutableDictionary,其中两个元素是相同的。调用 removeObject: 删除了两个条目。因此,对于 NSMutableDictionary 来说,isEqual 使用字典元素的内容,而不仅仅依赖于匹配对象的内存。
    【解决方案3】:

    但是所有%ps 打印都给出了相同的值。这是为什么呢?

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

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

    在第二个for循环中,nextObjectNSDictionaries的地址与第一个for循环中打印的地址不同

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

    当我遍历[msgDetailArray copy],然后执行removeObject 时,它会将其从原始msgDetailArray 中删除。

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

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

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

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

    【讨论】:

    • 很好的解释。只是为了确认当我调用 [ removeObject:] 时,它知道要删除哪个对象,因为它正在使用 NSMutableDictionary 的内容。那是对的吗?如果是,我假设这仅适用于某些对象,如 NSDictionary,而不适用于所有对象(具有自定义 isEqual 的东西)。是否有一些我可以查找的术语(如符合 NSCopying 协议)可以用来区分我正在处理的对象类型?
    • 所有的解释都很好,我不知道该接受哪一个!我将在明天之前接受最高票数的人!
    • @SmartHome removeObject 知道要删除什么对象,因为它是浅拷贝,这意味着两个容器中的字典,原始和副本,实际上是内存中的相同对象。它们具有相同的地址、相同的内容等。这就是为什么删除它们没有问题。
    • 谢谢。我做了一个实验,我创建了一个 NSMutableDictionary,其中两个元素是相同的。调用 removeObject: 删除了两个条目。因此,对于 NSMutableDictionary 来说,isEqual 使用字典元素的内容,而不仅仅依赖于匹配对象的内存。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-20
    • 2023-03-15
    • 1970-01-01
    • 2011-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多