2

NSMutableArray がロードされた NSMutableDictionary を調べようとしていますが、めちゃくちゃになっていて、方法がわかりません。ゲームの質問のより大きな plist を読み込もうとしていますが、適切なレベルでない場合は削除します。削除しようとするまでエラーは発生しません。誰かがこのコードの欠陥を見ることができますか? 私はそれを非常に感謝します!

ありがとう、

~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];   
}
}
4

3 に答える 3

10

他のすべての人が言及した他の問題を除いて、範囲外エラーが発生する理由に焦点を当てましょう。

関連するコードは次のとおりです。

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]

2 回目の反復:

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

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

3 回目の反復:

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

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

4 回目の繰り返し:

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

問題が見えますか?あなたの for ループは、インデックスでオブジェクトにアクセスすることを前提としていますが、各反復の後、配列から何かを削除しているため、その時点以降のすべてのインデックスがシフトされます。removeObjectAtIndex:同時に配列をウォークスルーしようとしているため、 を呼び出すべきではありません。

特定のオブジェクトをスキップしようとしているだけの場合は、そのオブジェクトに到達したときに「続行」を呼び出すことができます。実際に配列から削除したい場合は、 を呼び出すだけ[QuestionsByLevel removeObject:GameLevel]です。または、あなたの状況に適したものなら何でも。ただし、配列を反復処理する前にそれを行ってください。

于 2009-11-02T06:05:04.780 に答える
9

This is not an answer. This is a critique of your code.

  1. Holy memory leaks, Batman! You alloc/init: GameLevel, Components, and level, but never release any of them.
  2. GameLevel doesn't need to be alloc/init'd at all. You can just pull the value out of [settings objectForKey:kLevelKey];, assign it into your GameLevel string, and use that. Then you don't even have to release it.
  3. Your loop is... odd. You're iterating through the loop, but each time you iterate, you set the self.QDetailsByLevel property to a new value. Are you sure that's what you want?
  4. This: if (level != GameLevel) does not do what you think it does. That's comparing pointers (ie, the ADDRESSES of two objects in memory). In your current state, both level and GameLevel have been alloc/init'd, which means they will never be the same object. You're probably wanting if ([level isEqualToString:GameLevel] == NO) instead.
  5. You subtract one from [self.QuestionsByLevel count] to get your QuestionCount int, which would appear to be an upper bound on a for() loop. Yet the conditional on the for loop (which @Michael showed to be your problem) subtracts another 1 from QuestionCount, which means that your for() loop will never reach the last element in the array. Are you sure that's what you want?
  6. Memorize this: http://www.cocoadevcentral.com/articles/000082.php (or this)
于 2009-11-02T05:02:12.407 に答える
3

私が間違っていなければ、問題は、オブジェクトの内容を反復処理しているときにオブジェクトを変更しているという事実が原因で発生しています。リストからオブジェクトを削除すると、終了条件が無効になります。これをwhileループとして実装してみてください。リストから要素を削除するたびに、終了条件を更新するように注意してください。デバッガーを使用して、範囲外の場所を見つけることもできます。

于 2009-11-02T04:42:36.053 に答える