【问题标题】:SpriteKit - enumerateChildNode causes crash "array was mutated while being enumerated."SpriteKit - enumerateChildNode 导致崩溃“数组在被枚举时发生了变异。”
【发布时间】:2017-11-15 20:03:25
【问题描述】:

我正在玩弄一些 SpriteKit 的东西。我对SpriteKit 很陌生,所以我的技术可能不好,我不知道。这是我正在做的事情:

它基本上是一个带盾牌的宇宙飞船(2D,从上面看)。有几个“屏蔽段”(左、右、上、下,每个都是SKShapeNode),然后是“移相器”nodes,它们是从设备的边界到中心绘制的线(宇宙飞船在中心)。我想检测移相器何时击中屏蔽段之一。我尝试使用collisionMasks,但它根本不起作用。所以我尝试了自己的检测方式。

接下来,我绘制相位器的方法可能看起来很奇怪。我在网上搜索,但没有找到任何东西,真的。所以我绘制移相器的方式是不断地用稍长的路径替换phaser node(一条直线)的路径,每一帧。

这就是执行此操作并导致应用崩溃的方法:

incomingPhasers 是一个数组,包含当前场景中的所有Phaser 对象。 Phaser 类主要包含SKShapeNode 称为node 和其他一些元信息。

移相器进度的推进是通过将progress属性增加一点来完成的。 delta 是自上一帧以来的时间差(因为此方法是从 update(_:) 调用的

let vector是用于画线的位移矢量。

private func advancePhasers(_ delta: TimeInterval) {
    for phaser in incomingPhasers where !phaser.targetHit {
        // advance progress of phaser
        phaser.progress = min(phaser.progress + CGFloat(delta) * phaser.progressRate, 1)
        let vector = phaser.origin.vector(toPoint: phaser.target, fraction: phaser.progress)

        // create new path
        let path = UIBezierPath()
        path.move(to: phaser.origin)
        path.addLine(to: phaser.origin + vector)
        phaser.node.path = path.cgPath

        // check collision
        let phaserPoint = path.currentPoint
        enumerateChildNodes(withName: "shieldSegment", using: { (node, stop) in
            if let node = node as? ShieldSegmentNode {
                if node.contains(phaserPoint) {

                    // collision
                    phaser.targetHit = true

                }
            }
        })
    }
}

enumerateChildNodes 在我向数组中添加许多移相器时导致应用程序崩溃:

* 由于未捕获的异常“NSGenericException”而终止应用程序,原因:“* 集合 <0x17404ce40>

    标签: ios swift multithreading sprite-kit


    【解决方案1】:

    您不能在枚举集合时对其进行变异(更改、添加或删除项目)。很难告诉你更多关于如何在你的代码中没有更多细节的情况下修复它。有些事情正在改变这里的集合:

    enumerateChildNodes(withName: "shieldSegment", using: { (node, stop) in
        if let node = node as? ShieldSegmentNode {
            if node.contains(phaserPoint) {
    
                // collision
                phaser.targetHit = true
    
            }
        }
    })
    

    我解决这些问题的一个方法是我有第二个集合,用于将符合我期望标准的项目放入其中,然后在第一次枚举之后,我可以枚举第二个集合并对第一个集合执行必要的突变

    【讨论】:

    • 嗯,但是什么?这些只是读取,而唯一的写入操作是phaser.targetHit = true。但即使我删除它,它也会崩溃。我会试着找出答案。编辑:即使它的主体是空的,它也会崩溃。这很奇怪
    • 它一定在其他地方发生了变异,可能在不同的线程中
    • 嗯,还有两个地方我用enumerateChildNodes。但我不会在另一个线程上调用它们。所有这些都是从update(_:) 内部调用的。这不是在主队列上吗?
    • 您可以尝试使用objc_sync_enter(self.childNodes) 执行枚举锁定childNodes,然后使用objc_sync_exit(self.childNodes),这将阻止其他人在您枚举时访问childNodes。但这也会导致其他访问线程阻塞,直到它被释放
    • 干得好!你做到了!
    猜你喜欢
    • 2014-07-04
    • 2017-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-01
    • 2016-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多