@synchronized不能在NSMutableArray上工作

时间:2012-12-03 02:08:38

标签: objective-c ios multithreading cocos2d-iphone

我试图从一个可变数组中删除和对象 - 一个遍历每一帧的数组(参见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 个答案:

答案 0 :(得分:9)

您需要在迭代时完全锁定数组的更新。将同步块添加到方法addEventSubscriber:removeEventSubscriber:将不起作用,因为迭代时数组可以更改,因为迭代未同步。简而言之,这三种方法中只有一种可以同时运行。

您可以使用@synchronizedNSLock在迭代过程中手动锁定数组更新。

或者,您可以将GCD与串行调度队列一起使用,以确保一次只执行一个方法。这是如何工作的:

您还可以将队列存储为您正在进行此处理的类对象的属性。

// 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];
           }
       }
    });
}

答案 1 :(得分:0)

您只是在从阵列中删除项目时获取锁定,而不是在枚举项目时获取锁定。该错误表明,在枚举中,您尝试删除项目,这是锁定但不是枚举所允许的。

在枚举之前简单地锁定数组可能也不起作用。相同的线程可以递归地锁定对象,但如果您的枚举和删除位于不同的线程上,那么尝试在枚举中删除会导致死锁。如果您遇到这种情况,则需要重新考虑您的模型。

答案 2 :(得分:0)

我经常遇到这个问题。除了本科OS课程之外,我没有线程处理/同步的经验,所以这就是我提出的。

每次迭代对象列表并想要删除某些内容时 - 而是将该对象添加到全局“objectsToRemove”数组中。在update方法中,从objectsToRemove中删除所有内容,然后清理数组以避免在下次更新时过度删除对象。

Cocos2D有一个CCArray,它本质上是一个带有一些附加功能的NSMutableArray,比如能够在迭代时删除一个项目。我自己没有读过代码,所以我不确定它是如何实现的,因此我不使用它。

答案 3 :(得分:-1)

你需要在这个函数中添加synchronized来。

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