NSArray的异常行为

时间:2012-02-02 07:17:10

标签: objective-c cocoa

我有以下代码

NSArray *myArray = [[NSArray alloc] initWithObjects:@"1",@"2",nil];
NSMutableDictionary *dic0 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag", @"name", myArray, @"arraye",nil];
NSMutableDictionary *dic1 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag1", @"name", myArray, @"arrayr",nil];
NSMutableDictionary *dic2 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag2", @"name", myArray, @"arrayq",nil];
NSMutableDictionary *dic3 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag2", @"name", myArray, @"arrayqe",nil];
NSArray *array12424 = [[NSArray alloc] initWithObjects:dic0, dic1, dic2, dic3];

NSLog(@"array12424 %d", [array12424 retainCount]);
NSLog(@"array12424 %@", array12424);
int j = [myArray retainCount];
for(int i=0;i<j; ++i)
{
    [myArray release];
        NSLog(@"%d", i);
    NSLog(@"myArray %@", myArray);
}

NSLog(@"array12424 %@", array12424);  

释放myArray后,我预计会崩溃;为什么arraye和其他数组键指向array12424?

//output  

  2012-02-02 12:33:58.454 212121212[6924:a0f] array12424 1
    2012-02-02 12:33:58.459 212121212[6924:a0f] array12424 (
            {
            arraye =         (
                1,
                2
            );
            name = parag;
        },
            {
            arrayr =         (
                1,
                2
            );
            name = parag1;
        },
            {
            arrayq =         (
                1,
                2
            );
            name = parag2;
        },
            {
            arrayqe =         (
                1,
                2
            );
            name = parag2;
        }
    )
    2012-02-02 12:33:58.459 212121212[6924:a0f] 0
    2012-02-02 12:33:58.460 212121212[6924:a0f] myArray (
        1,
        2
    )
    2012-02-02 12:33:58.460 212121212[6924:a0f] 1
    2012-02-02 12:33:58.460 212121212[6924:a0f] myArray (
        1,
        2
    )
    2012-02-02 12:33:58.461 212121212[6924:a0f] 2
    2012-02-02 12:33:58.462 212121212[6924:a0f] myArray (
        1,
        2
    )
    2012-02-02 12:33:58.463 212121212[6924:a0f] 3
    2012-02-02 12:33:58.463 212121212[6924:a0f] myArray (
        1,
        2
    )
    2012-02-02 12:33:58.464 212121212[6924:a0f] 4
    2012-02-02 12:33:58.466 212121212[6924:a0f] myArray myArray 
    2012-02-02 12:33:58.466 212121212[6924:a0f] array12424 (
            {
            arraye = "array12424 ";
            name = parag;
        },
            {
            arrayr = "array12424 ";
            name = parag1;
        },
            {
            arrayq = "array12424 ";
            name = parag2;
        },
            {
            arrayqe = "array12424 ";
            name = parag2;
        }
    )

我使用的是MAC osx 10.6 base SDK。

5 个答案:

答案 0 :(得分:4)

0)不要使用retainCount

1)Cocoa Collections保留其内容,然后在删除时释放它们,或者当集合被销毁时释放它们。此外,没有明确说明您调用的API的文档不会自动发布其参数。

2)您没有正确初始化数组 - 必须终止为止:NSArray *array12424 = [[NSArray alloc] initWithObjects:dic0, dic1, dic2, dic3];

3)简单地说,没关系 - 它是未定义的行为。结果无法预测(除非涉及猛禽)。它在其他操作系统版本中崩溃。它会像你期望的那样在启用GuardMalloc时崩溃。之后不久就会崩溃(如果你很幸运的话)。

答案 1 :(得分:1)

拿2,让我们带您了解更详细的代码。首先,我们需要修复3个警告,并加强日志记录:

+ (void) test
{
   NSArray *myArray = [[NSArray alloc] initWithObjects:@"1",@"2",nil];
   NSMutableDictionary *dic0 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag", @"name", myArray, @"arraye",nil];
   NSMutableDictionary *dic1 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag1", @"name", myArray, @"arrayr",nil];
   NSMutableDictionary *dic2 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag2", @"name", myArray, @"arrayq",nil];
   NSMutableDictionary *dic3 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"parag2", @"name", myArray, @"arrayqe",nil];
   NSArray *array12424 = [[NSArray alloc] initWithObjects:dic0, dic1, dic2, dic3, nil];

   NSLog(@"array12424 %lu", [array12424 retainCount]);
   NSLog(@"array12424 %p, %@", array12424, array12424);
   NSUInteger j = [myArray retainCount];
   for(NSUInteger i=0;i<j; ++i)
   {
      NSLog(@"%lu, %lx", i, [myArray retainCount]);
      [myArray release];
      NSLog(@"%lu, %lx", i, [myArray retainCount]);
      NSLog(@"myArray %p, %@", myArray, myArray);           // breakpoint here
   }

   NSLog(@"array12424 %p, %@", array12424, array12424);  
}

在指定的行上放置一个断点并调用该方法。

当你第一次在控制台上看到以下内容时点击断点:

2012-02-03 07:36:34.651 sandpit[32581:903] array12424 1
2012-02-03 07:36:34.659 sandpit[32581:903] array12424 0x100176820, (
        {
        arraye =         (
            1,
            2
        );
        name = parag;
    },
        {
        arrayr =         (
            1,
            2
        );
        name = parag1;
    },
        {
        arrayq =         (
            1,
            2
        );
        name = parag2;
    },
        {
        arrayqe =         (
            1,
            2
        );
        name = parag2;
    }
)
2012-02-03 07:36:34.660 sandpit[32581:903] 0, 5
2012-02-03 07:36:34.661 sandpit[32581:903] 0, 4

首先看一下最后一行,我们看到保留计数是5.现在我们创建并拥有了数组 - 这是1 - 我们已经将它添加到4个字典中并且它们保留了它们的参数 - 这是另外4个。这5个中的任何一个实际上是否会产生误导,是否会有一个待定的autorelease或两个会使它掉线?在这种情况下,我们可以确定不是因为我们没有自动释放,并且集合保留了它们的参数。

在调试器变量显示中,查看myArray

的条目
myArray = (NSCFArray *) 0x100513aa0 2 objects

这告诉我们引用我的myArray的Obj-C对象确实是一个数组,它位于内存地址0x100513aa0并包含两个项目。您将在控制台中看到该地址,因为将'%p'添加到某些NSLog中。

现在按几次继续,直到最后一个控制台输出为:

2012-02-03 07:37:33.074 sandpit[32581:903] 4, 1
2012-02-03 07:37:33.074 sandpit[32581:903] 4, fffffffffffffff

现在您可以看到为什么我使用`%lx'来打印保留计数而不是匹配其类型的'%lu'(无符号64位整数)。保留计数显然消极了!

真正发生的事情是前一个release将计数发送到0然后内存可以重新使用 - 所有这些NSLog都是格式化字符串并需要内存,所以有一些流失。

现在查看调试器中的myArray条目 - 它是否仍然说它是一个包含2个对象的数组?可能不是。我用各种伪装运行这个代码,它认为它是一个空数组,一个带有2个空值的数组,一个字符串......你在问题中看到了什么 - NSLog输出中的字符串......它指向释放内存,它可以包含任何内容。

Obj-C中存在 NO 内存安全性,您可以根据需要随意读写内存。不能保证像过度释放内存那样会导致立即崩溃,或者确实发生任何崩溃,但应用程序很可能会以某种方式出现故障。

HTH。

如果你想深入挖掘,你可以编写自己的代码来打印显示所包含对象的地址(使用'%p')的字典,或者只是深入调试器中的字典,这样你就可以看到共享

强制性健康警告retainCount学习时非常有用,但永远不会,在实际代码中使用它确定是否需要release d,因为可能存在您不知道的待定减量(来自autorelease)。

答案 2 :(得分:0)

首先在此声明中添加nil

NSArray *array12424 = [[NSArray alloc] initWithObjects:dic0, dic1, dic2, dic3,nil];

现在遇到崩溃只需更改此代码

for(int i=0;i<j; ++i)
{
    [myArray release];
    NSLog(@"%d", i);
    NSLog(@"let the system remove this array, give him some time dude to remove it, then see what happens");
    NSLog(@"myArray %@", myArray);
}

答案 3 :(得分:-1)

我假设这个问题是要了解可可内部工作......

保留计数0并不意味着对象被删除...在保留计数达到0之后,cocoa内部有自己的逻辑来调用对象上的dealloc。

你可以尝试在不同的事件上下文中调用last语句...通常在事件上下文之后使用对象。

答案 4 :(得分:-1)

我的猜测是你正试图了解内存分配&amp;解除分配,这不是您打算在实际应用程序中放置的代码。

在这种情况下,尝试使用%p格式打印对象引用进行探索 - 这将打印对象的地址。然后,您将能够看到哪些对象真正被共享(而不仅仅是具有相同的内容)以及何时重用内存等。或者使用调试器为您提供相同的功能。

在探索时,可以放弃避免retainCount的一般建议。