0

I am currently developing a game where spheres are falling from the sky. While collecting spheres you gain points and after a certain amount of points all spheres accelerate to another speed.

  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")
            }
    }

I am not sure if I have understood GCD correctly, I have tried multiple solutions and this is the one I was sure was going to work. I always end up with the same error message:

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

How do I get the threads to not interfere with each other while handling the array?

Pang
  • 9,564
  • 146
  • 81
  • 122
hardyfelix
  • 212
  • 3
  • 11

2 Answers2

0

I'm not sure if this is the issue, but from the documents for:

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.

I bolded the important part here. Why don't you call the loop with a dispatch_barrier_sync instead.

Jeremy Pope
  • 3,342
  • 1
  • 16
  • 17
  • I tried calling the loop with dispatch_barrier_sync but the problem remains. – hardyfelix Feb 26 '15 at 09:25
  • I commented out everything that had to do with the array, and I still got the same error message. This is very strange since no other array is manipulated like that. How can I debug this? The error message "Collection <__NSArrayM: 0x17004c9f0> ..." shows an ID to an array, but when looking through the code the arrays are not given any IDs. How can I find the particular array that is behind handled thus giving this error message? – hardyfelix Feb 26 '15 at 09:47
  • I commented out literally everything. I now only start 1 thread that adds more spheres and then sleeps for a duration. I always end up with the same error message when the thread sleeps, nothing else is running but the sleeping thread. Could there be any problem with the sleep function inside a thread? But nothing like that should produce the "<__NSArrayM: 0x170048e20> was mutated while being enumerated.'" – hardyfelix Feb 26 '15 at 10:47
0

My problem was that I was using a thread-sleep solution to fire new spheres in a time interval. This was a bad choice but should not have produced such an error message in my opinion. I solved it using NSTimer to fire new spheres in a time interval. This gave the game a bit of a lag, but it is more robust and won't crash. Next up is finding out how to use the NSTimer without creating such a lag in the game!

hardyfelix
  • 212
  • 3
  • 11