【问题标题】:NSMutable Array - objectAtIndex: index beyond boundsNSMutableArray - objectAtIndex:索引超出范围
【发布时间】:2009-11-02 04:33:04
【问题描述】:

我正在尝试查看加载了 NSMutableArray 的 NSMutableDictionary,但我把它搞砸了,我不知道该怎么做。我正在尝试加载更大的游戏问题列表,如果它们不是正确的级别,则将其删除。在我尝试删除之前,我没有收到错误消息。任何人都可以看到这段代码中的缺陷吗?非常感谢!

谢谢,

~G

{
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
NSString *GameLevel = [[NSString alloc] initWithFormat: [settings objectForKey:kLevelKey]];

NSBundle *Bundle = [NSBundle mainBundle];
NSString *PListPath = [Bundle pathForResource:@"questions" ofType:@"plist"];

NSMutableDictionary *Dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:PListPath]; 

self.QuestionDetailsByLevel = Dictionary;
[Dictionary release];

NSMutableArray *Components = [[NSMutableArray alloc] initWithArray:[QuestionDetailsByLevel allKeys]];
self.QuestionsByLevel = Components;

int QuestionCount = [self.QuestionsByLevel count] - 1;

for (int j = 0; j < QuestionCount - 1; j++)
{

    NSString *SelectedQuestion = [self.QuestionsByLevel objectAtIndex:j];
    NSMutableArray *Array = [QuestionDetailsByLevel objectForKey:SelectedQuestion];
    self.QDetailsByLevel = Array;

    NSString *level = [[NSString alloc] initWithFormat:[self.QDetailsByLevel objectAtIndex:Level]];

    if (level != GameLevel)
        [QuestionsByLevel removeObjectAtIndex:j];   
}
}

【问题讨论】:

  • 有什么理由把所有东西都放在一个 plist 中吗?每个级别只需一个,然后加载您需要的一个。

标签: objective-c cocoa nsmutablearray nsmutabledictionary


【解决方案1】:

除了其他人提到的其他问题,让我们专注于为什么会出现越界错误。

这是相关的代码。

for (int j = 0; j < QuestionCount - 1; j++)
{

    NSString *SelectedQuestion = [self.QuestionsByLevel objectAtIndex:j];
    // ... snip ...
    if (level != GameLevel) //Always happening in your current code
        [QuestionsByLevel removeObjectAtIndex:j];       
}

让我们看看这段代码经过几次迭代后会发生什么。

第一次迭代:

j == 0
self.QuestionsByLevel == [Q1, Q2, Q3, Q4, Q5]

SelectedQuestion = QuestionsByLevel[0] // Q1

// The following happens because you call removeObjectAtIndex:0
QuestionsByLevel = [Q2, Q3, Q4, Q5]

第二次迭代:

j == 1
self.QuestionsByLevel == [Q2, Q3, Q4, Q5]
SelectedQuestion = QuestionsByLevel[1] // Q3

// The following happens because you call removeObjectAtIndex:1
QuestionsByLevel = [Q2, Q4, Q5]

第三次迭代:

j == 2
self.QuestionsByLevel == [Q2, Q4, Q5]
SelectedQuestion = QuestionsByLevel[2] // Q5

// The following happens because you call removeObjectAtIndex:2
QuestionsByLevel = [Q2, Q4]

第四次迭代:

j == 3
self.QuestionsByLevel == [Q2, Q4]
SelectedQuestion = QuestionsByLevel[3] // CRASH!!!! 

你能看出问题吗?您的 for 循环假定您将通过索引访问对象,但是在每次迭代之后,您将从数组中删除一些内容,这会在该点之后移动所有索引。你不应该打电话给removeObjectAtIndex:,因为你正试图同时遍历数组。

如果您只是想跳过特定对象,则可以在到达该对象时调用“继续”。如果您真的想将其从阵列中删除,只需致电[QuestionsByLevel removeObject:GameLevel]。或者任何对你的情况有意义的东西。但是在遍历数组之前这样做。

【讨论】:

    【解决方案2】:

    这不是答案。这是对您的代码的批评。

    1. 神圣的内存泄漏,蝙蝠侠!你分配/初始化:GameLevelComponentslevel,但永远不要释放它们中的任何一个。
    2. GameLevel 根本不需要分配/初始化。您可以从[settings objectForKey:kLevelKey]; 中提取值,将其分配到您的GameLevel 字符串中,然后使用它。那么你甚至不必释放它。
    3. 你的循环是……奇怪的。您正在迭代循环,但每次迭代时,您都将 self.QDetailsByLevel 属性设置为一个新值。你确定这就是你想要的吗?
    4. 这个:if (level != GameLevel) 并没有按照你的想法去做。那是比较指针(即内存中两个对象的地址)。在您当前的状态下,levelGameLevel 都已分配/初始化,这意味着它们将永远是同一个对象。您可能想要的是 if ([level isEqualToString:GameLevel] == NO)
    5. 您从[self.QuestionsByLevel count] 中减去一个以得到您的QuestionCount int,这似乎是for() 循环的上限。然而,for 循环的条件(@Michael 表明这是你的问题)从QuestionCount 中减去 another 1,这意味着你的 for() 循环永远不会到达数组中的最后一个元素。你确定这就是你想要的吗?
    6. 记住这个:http://www.cocoadevcentral.com/articles/000082.php(或this

    【讨论】:

    • 另外,不要让用户提供格式字符串。 initWithFormat:[settings objectForKey…] 总是错的。用户 will 编写一个有效的格式字符串,但需要您没有提供的参数。然后你的应用程序将崩溃。始终传递常量字符串 (@"…") 或字符串的本地化版本 (NSLocalizedString(@"…", /*comment*/ @"Explanation of the string")
    • 我也质疑存在两个不同类型的属性,QuestionDetailsByLevelQDetailsByLevel。如你所愿,你应该选择一个或另一个,或者给他们一个或两个更好的名字。
    • 如果这是在垃圾收集下的 Mac 上运行,那么第 1 点没有实际意义。但是,我从不假设垃圾收集,因为知道如何手动管理内存是一项非常有价值的技能。
    • 确实如此。了解如何手动管理内存是值得的,因为如果您曾经为框架、库、插件(任何类型,包括颜色选择器和图像单元)编写代码,您必须能够做到这一点,或 iPhone 应用程序。
    【解决方案3】:

    如果我没记错的话,问题正在发生,因为您在迭代对象的内容时正在修改对象。当您从列表中删除对象时,您的终止条件将变为无效。尝试将其实现为 while 循环,并在从列表中删除元素时小心更新终止条件。您还可以使用调试器找出超出范围的位置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多