【问题标题】:SpriteKit. A change from optional to force unwrap crashes the app精灵套件。从可选更改为强制展开会使应用程序崩溃
【发布时间】:2017-01-03 20:04:40
【问题描述】:

我正在关注一个关于 IF 语句有问题的 SpriteKit 教程。该行的逻辑如下:如果子弹和小行星相撞,则将它们移除。

if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid {
   // remove bullet and asteroid
}

在尝试确保小行星 (body2.node) 在关闭之前位于可播放区域内时会出现问题。为此,作者添加了以下内容:

body2.node?.position.y < self.size.height

如下制作完整的 IF 语句:

if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid && body2.node?.position.y < self.size.height {
   // remove bullet and asteroid
}

显然,该行适用于 Swift 2,但是 Swift 3 进行了更正,将位置从可选位置更改并强制展开位置。

    if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid && body2.node!.position.y < self.size.height {
        // remove bullet and asteroid        
    }

通过强制展开位置,当三个物体发生碰撞时,应用程序会以“I THINK”的形式崩溃。看着屏幕真的很难分辨。

我正在测试下面的代码,目前还没有遇到任何问题。你们认为下面的修复会起作用吗?我在想的是,如果我确保 body2.node 不是 nil,那么应用程序没有理由崩溃,因为在尝试强制解包时不会遇到 nil。

if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid {
    // If the bullet has hit the asteroid
  if body2.node != nil {
    if ( body2.node!.position.y < self.size.height ) {
       // remove bullet and asteroid
    }           
  }           
}

或者,如果你们可以提出另一种方法来编写原始 IF 语句?

谢谢

【问题讨论】:

  • 当三个物体同时接触并且其中一个或多个在 didBegin(_contact) 中被移除而没有等待物理引擎处理所有物体时,游戏很可能会崩溃发生的接触。我只是在这里做了一个解释:stackoverflow.com/a/41452239/3402095 在强制展开之前检查 body.node 是否为 nil 将防止崩溃。同样这样,可能会跳过一些 didBegin(_contact) 调用,因此您必须检查这是否对您的游戏有效。
  • 要了解guard 的低谷,请查看:stackoverflow.com/a/39263210/2109038
  • 要详细了解 if let 正在做什么,请看这里:stackoverflow.com/a/36303241/2109038

标签: sprite-kit swift3


【解决方案1】:

是的,if != nil 语句(如目前所写的那样)将防止强制解包引起的崩溃。

另一种方法是在 Swift 中使用 if let 语法:

if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid {
    // If the bullet has hit the asteroid
    if let body2Node = body2.node {
        if body2Node.position.y < self.size.height {
           // remove bullet and asteroid
        }           
    }           
}

好处是它从您的代码中删除了!,并且更清楚地将 nil 检查与您稍后使用的变量联系起来。

【讨论】:

    【解决方案2】:

    nathan 有正确的答案,但更好的选择是使用 guard 来保护您的函数:

    ...
    
    guard let  body1Node = body1.node, let  body2Node = body2.node else {return}
    //Beyond this point we need to guarentee both nodes exist
    
    if body1.categoryBitMask == PhysicsCategories.bullet && body2.categoryBitMask == PhysicsCategories.asteroid {
        // If the bullet has hit the asteroid
        if body2Node.position.y < self.size.height {
           // remove bullet and asteroid
        }           
    
    }
    

    通过使用守卫,我们减少了嵌套,并且我们知道,超出这一点,物理体必须包含一个节点,除非我们在函数结束之前将其删除,这将消除进一步检查节点是否存在的需要。

    作为旁注,我始终建议在更新阶段结束时删除节点以避免此类问题,而只是以某种方式标记节点,您将不再使用它。

    【讨论】:

    • 退货去了哪里,如何确保安全处理不满足要求?
    • @Confused 别留下没用的 cmets,如果你不知道 return 的作用,google 一下或者去看看书
    • 这是一个严肃的问题。内森的回答处理了失败。据我所知,尽管您声称 guard's 具有优越性,但您的似乎无法处理。
    • 所以有链接。以及对理解更新、更高级技术的建议有一定帮助和帮助的答案历史。例如:stackoverflow.com/a/39263210/2109038。或者,您可以多花 20 秒的时间来提供有关 guard 如何创建存在于其 else 范围之外的变量的信息,并揭示 guard's 上下文(范围)在函数内的强大功能,因此在 else{} 闭包中使用 return 的力度,或者您可以花更多时间尝试指导我您认为 SO 是什么,不是什么。
    • 你可以做 2 个不同的守卫,或者在 else 里面检查哪个变量是 nil
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-13
    相关资源
    最近更新 更多