0

3つのクラスに問題があります。最初のクラスは と呼ばれPlayerます。このクラスには、NSMutableArrayと呼ばれる内部がありunitsます。この配列は、クラスのオブジェクトで構成されていますUnit。このクラスには、NSMutableArray呼び出されbulletsた . それはこのように動作します:

ある時点で、クラスPlayer(ViewController代わりに である可能性があります) は にオブジェクトを追加しますunits。次に、 のインスタンスUnitが初期化されると、上記の結果として、毎秒弾丸の作成を担当する NSTimer が作成されます。

問題は、この途中のどこかでクラッシュし、SIGABRT次の理由で例外が発生したCollection <__NSArrayM: 0xb1a2970> was mutated while being enumerated.ことを示していることです。どういう意味ですか!

動作する可能性のある実行可能コードの一部を次に示します。

ViewController.h (プレイヤーの代わり)

@interface ViewController : UIViewController
{
    NSMutableArray *units;
    NSTimer *updateTimer;
}
-(void)Update;

ViewController.m

@implementation ViewController
//methods...

- (void)viewDidLoad
{
    //more default code

    //Initialized array and adds one object with the default constructor for simplicity
    units = [[NSMutableArray alloc] initWithObjects:[[Unit alloc] init], nil]
}

-(void)Update
{
    for(Unit *unit in units)
    {
        [unit Update];
        if(unit.deleteFromList)
            [units removeObject:unit];   
    }
}
//More methods
@end

Unit.h

@interface Unit : NSObject
{
    NSMutableArray *bullets;
    NSTimer *bulletTimer;
    boolean deleteFromList;
}

@property(readonly, assign)deleteFromList;

-(void)Fire;

-(void)Update;

単位.m

@implementation Unit

@synthesize deleteFromList;

-(id)init
{
    if(self)
    {
        bullets = [[NSMutableArray alloc] init];
        bulletTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(fire) userInfo:NULL repeats:true];
        deleteFromList = false;
    }

    return self;
}

-(void)Fire
{
    [bullets addObject:[[Bullet alloc] init]];
}

-(void)Update
{
    for(Bullet *bullet in bullets)
    {
        [bullet Update];
        if(bullet.deleteFromList)
            [bullets removeObject:bullet];
    }

    if(certainCondition)
        deleteFromList = true;
}

中身はどうなるか関係ないので、弾丸クラスは省略します。また、残りはこの例では役に立たないため、すべてのクラスとコンストラクターが短縮されました

編集:

追加するのを忘れていたもう 1 つのことは、追加しようとしている update メソッドの NSMutableArray ユニットの列挙でタイマーが作成されることです。また、Unit と Bullet に、削除するように命令する変数を追加しています。箇条書きの更新は位置を変更し、deleteFromList 変数も変更します

4

3 に答える 3

4

2 つの簡単な方法があります。1 つは削除するオブジェクトの配列を作成する方法で、もう 1 つは元の方法のコピーを反復処理する方法です。この 2 番目の手法は、多少読みやすくなります。

方法 1

NSMutableArray *toRemove = [NSMutableArray array];
for (Bullet *bullet in bullets)
{
    [bullet Update];
    if (bullet.deleteFromList)
    {
        [toRemove addObject:bullet];
    }
}
[bullets removeObjectsInArray:toRemove];

方法 2

for (Bullet *bullet in [bullets copy])
{
    [bullet Update];
    if (bullet.deleteFromList)
    {
        [bullets removeObject:bullet];
    }
}

最初のアプローチは少し冗長ですが、元の配列のコピーを作成しません。元の配列が非常に大きく、パフォーマンスやメモリの理由でそのコピーを作成したくない場合は、最初の方法が最適です。それが問題にならない場合 (99% の場合)、私は 2 番目のアプローチを好みます。


余談ですが、Objective-C では、メソッド名が略語で始まる場合を除き、Objective-C ではメソッド名を大文字で始めるべきではURLあり[bullet Update]ません[bullet update]

于 2012-05-30T02:41:03.093 に答える
4

NSMutableArrayfor ループ中または列挙中のアイテムを削除することはできません。

ドキュメント:列挙中に変更可能なコレクションを変更するのは安全ではありません。一部の列挙子は現在、変更されたコレクションの列挙を許可する場合がありますが、この動作は将来サポートされる保証はありません。

for(Bullet *bullet in bullets)
{
    [bullet Update];
    if(bullet.deleteFromList)
        [bullets removeObject:bullet];
}

NSMutableArray *toRemove = [NSMutableArray array];
for(Bullet *bullet in bullets)
{
    [bullet Update];
    if(bullet.deleteFromList)
        [toRemove addObject:bullet];
}
[bullets removeObjectsInArray:toRemove];
于 2012-05-30T02:08:21.080 に答える
0

または

NSIndexSet *indexSet = [units indexesOfObjectsPassingTest:^BOOL(Unit *udit, NSUInteger idx, BOOL *stop) {
    [unit Update];
    return unit.deleteFromList;
}];

[units removeObjectsAtIndexes:indexSet];
于 2012-05-30T02:18:29.703 に答える