Swift - GCD 可变数组多线程问题 "mutated while being enumerated"
Swift - GCD mutable array multiple threads issue "mutated while being enumerated"
我目前正在开发一款球体从天而降的游戏。在收集球体时,您会获得积分,并且在获得一定数量的积分后,所有球体都会加速到另一种速度。
- New spheres are continuously added to an Array (4 Spheres inside each SKNode).
- When they are to accelerate I iterate through the array to increase the speed of all of them.
- 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 而不会在游戏中造成这种延迟!
我目前正在开发一款球体从天而降的游戏。在收集球体时,您会获得积分,并且在获得一定数量的积分后,所有球体都会加速到另一种速度。
- New spheres are continuously added to an Array (4 Spheres inside each SKNode).
- When they are to accelerate I iterate through the array to increase the speed of all of them.
- 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 而不会在游戏中造成这种延迟!