Swift - GCD 可变数组多线程问题 "mutated while being enumerated"

Swift - GCD mutable array multiple threads issue "mutated while being enumerated"

我目前正在开发一款球体从天而降的游戏。在收集球体时,您会获得积分,并且在获得一定数量的积分后,所有球体都会加速到另一种速度。

  1. New spheres are continuously added to an Array (4 Spheres inside each SKNode).
  2. When they are to accelerate I iterate through the array to increase the speed of all of them.
  3. When the spheres have fallen out of the screen I remove them from the Array.
class GameScene: SKScene, SKPhysicsContactDelegate {
...
var allActiveNodes = Array<SKNode>()
private let concurrentNodesQueue = dispatch_queue_create(
    "com.SphereHunt.allActiveNodesQueue", DISPATCH_QUEUE_CONCURRENT)
...

//1. This is where the new spheres are added to the Array via a new thread
func addSpheres(leftSphere: Sphere, middleLeftSphere: Sphere, middleRightSphere: Sphere, rightSphere: Sphere){
...
dispatch_barrier_async(self.concurrentNodesQueue){
        self.allActiveNodes.append(containerNode)
        let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
        dispatch_async(queue) {
//Set the new spheres in motion
            self.runPastAvatar(containerNode)
        }
    }

//2. This function starts a thread that will increase the speed of all active spheres
    func increaseSpeed20percent(){
        durationPercentage = durationPercentage * 0.8
        dispatch_sync(self.concurrentNodesQueue){
        let copyAllActiveNodes = self.allActiveNodes
        let count = copyAllActiveNodes.count

        for index in 0...count-1{
            let node = copyAllActiveNodes[index]
            node.removeAllActions()
            self.runPastAvatar(node)
        }
    }
}
//3. This method removes the sphere that is not in screen anymore from the Array
    func removeLastNode(node: SKNode){
    dispatch_barrier_async(self.concurrentNodesQueue){
            self.allActiveNodes.removeAtIndex(0)
            node.removeFromParent()
            println("Removed")
            }
    }

我不确定我是否正确理解了 GCD,我尝试了多种解决方案,而我确信这是可行的。我总是以相同的错误消息结束:

*** Terminating app due to uncaught exception 'NSGenericException', 
reason: '*** Collection <__NSArrayM: 0x17004c9f0> was mutated while being enumerated.'

如何让线程在处理数组时不相互干扰?

我不确定这是否是问题所在,但根据以下文档:

func dispatch_sync(_ queue: dispatch_queue_t,
             _ block: dispatch_block_t)

Unlike with dispatch_async, no retain is performed on the target queue. Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, no Block_copy is performed on the block.

> As an optimization, this function invokes the block on the current thread when possible.

我把重要的部分加粗了。为什么不用 dispatch_barrier_sync 来调用循环呢?

我的问题是我使用线程休眠解决方案在一定时间间隔内发射新球体。这是一个糟糕的选择,但在我看来不应该产生这样的错误信息。我使用 NSTimer 在一个时间间隔内发射新球体解决了这个问题。这让游戏有点滞后,但它更强大并且不会崩溃。接下来是找出如何使用 NSTimer 而不会在游戏中造成这种延迟!