1

可変配列からオブジェクトを削除しようとしています-すべてのフレームで反復される配列(tick:メソッドを参照)。

私は得ています

*コレクション <__NSArrayM: 0xaa99cb0> が列挙中に変異しました。

例外。

そのため、他のスレッドに触れないようにロックするように追加 @synchronized()しましたが、それでも失敗します。

- (void)addEventSubscriber:(id <EventSubscriber>)eventSubscriber
{
     [_eventSubscribers addObject:eventSubscriber];
}

- (void)removeEventSubscriber:(id <EventSubscriber>)eventSubscriber
{
    @synchronized(_eventSubscribers) // Not working.
    {
       [_eventSubscribers removeObject:eventSubscriber];
    }
}

- (void)tick:(ccTime)dt
{
   for (id <EventSubscriber> subscriber in _eventSubscribers)
   {
       if ([subscriber respondsToSelector:@selector(tick:)])
       {
           [subscriber tick:dt];
       }
   }
}
4

4 に答える 4

9

反復中は、アレイの更新を完全にロックする必要があります。同期されたブロックを両方のメソッドに追加addEventSubscriber:removeEventSubscriber:ます。反復が同期されていないため、反復中に配列が変更される可能性があるため、機能しません。簡単に言えば、一度に実行できるのはこれら3つのメソッドのうちの1つだけです。

@synchronized、またはを使用しNSLockて、反復中にアレイの更新を手動でロックできます。

または、シリアルディスパッチキューでGCDを使用して、一度に1つのメソッドのみが実行されるようにすることもできます。これがどのように機能するかです:

この処理を行っているクラスオブジェクトのプロパティとしてキューを保存することもできます。

// Create the queue
dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL);

- (void)addEventSubscriber:(id <EventSubscriber>)eventSubscriber
{
    dispatch_sync(myQueue, ^{
        [_eventSubscribers addObject:eventSubscriber];
    });
}

- (void)removeEventSubscriber:(id <EventSubscriber>)eventSubscriber
{
    dispatch_sync(myQueue, ^{
       [_eventSubscribers removeObject:eventSubscriber];
    });
}

- (void)tick:(ccTime)dt
{
    dispatch_sync(myQueue, ^{
       for (id <EventSubscriber> subscriber in _eventSubscribers)
       {
           if ([subscriber respondsToSelector:@selector(tick:)])
           {
               [subscriber tick:dt];
           }
       }
    });
}
于 2012-12-03T02:32:21.417 に答える
0

私はこの問題によく遭遇します。私は学部の OS コースを超えてスレッド処理/同期の経験がないので、これが私が思いついたものです。

オブジェクトのリストを反復処理して何かを削除したい場合は常に、代わりにそのオブジェクトをグローバルな「objectsToRemove」配列に追加します。update メソッドで、objectsToRemove からすべてを削除してから、配列をクリーンアップして、次の更新でオブジェクトが過剰に削除されないようにします。

Cocos2D には CCArray があります。これは基本的に NSMutableArray であり、反復中にアイテムを削除できるなど、いくつかの追加機能が追加されています。私は自分でコードを読んでいないので、どのように実装されているのかわからないため、使用していません。

于 2012-12-10T03:38:03.267 に答える
0

アイテムの列挙中ではなく、配列からアイテムを削除するときにのみロックを取得しています。このエラーは、列挙内でアイテムを削除しようとしていることを示唆しています。これは、列挙ではなくロックによって許可されています。

列挙する前に単に配列をロックしてもうまくいかない場合があります。同じスレッドでオブジェクトを再帰的にロックできますが、列挙と削除が異なるスレッドにある場合、列挙内で削除しようとするとデッドロックが発生します。このような状況にある場合は、モデルを再考する必要があります。

于 2012-12-03T02:15:15.183 に答える
-1

この関数にも同期を追加する必要があります。

- (void)tick:(ccTime)dt
{
  @synchronized(_eventSubscribers){
 for (id <EventSubscriber> subscriber in _eventSubscribers)
 {
   if ([subscriber respondsToSelector:@selector(tick:)])
   {
       [subscriber tick:dt];
   }
 }
 }
}
于 2012-12-03T02:10:25.010 に答える