【问题标题】:Swift: Unable to downcast AnyObject to SKPhysicsBodySwift:无法将 AnyObject 向下转换为 SKPhysicsBody
【发布时间】:2016-01-21 04:55:19
【问题描述】:

Apple 在 SKPhysicsBody 类中有如下方法。

 /* Returns an array of all SKPhysicsBodies currently in contact with this one */
    func allContactedBodies() -> [AnyObject]!

我注意到它返回了一个 AnyObject 数组。所以我读到了如何处理向下转换 AnyObject Here

我想遍历我的物理体的 allContactedBodies 数组。问题是,无论我尝试什么,我都无法正常工作。

我先尝试了这个:

for body in self.physicsBody.allContactedBodies() as [SKPhysicsBody] {

}

但我收到此错误。

致命错误:数组不能向下转换为派生数组

我也试过这个:

for object in self.physicsBody.allContactedBodies()  {
    let body = object as SKPhysicsBody
}

但这也会因以下原因而崩溃:

同样我也试过这个:

 for object in self.physicsBody.allContactedBodies()  {
     let body = object as? SKPhysicsBody

 }

没有崩溃,但是“body”变成了nil。

如果我根本不施放,我就不会崩溃。例如:

for object in self.physicsBody.allContactedBodies()  {

}

但显然,如果我想使用实际类型,我需要强制转换。


那么作为一个测试,我只是尝试了这个:

let object: AnyObject = SKPhysicsBody()
let body = object as SKPhysicsBody

这也会导致与图片中相同的崩溃。


但其他类型不会崩溃。例如,这不会崩溃。

let object: AnyObject = SKNode()
let node = object as SKNode

所以我的问题是,如何正确循环遍历 allContactedBodies 数组?

编辑:我在 iOS 8 beta 4 设备上运行 Xcode 6 beta 4。

编辑 2:更多信息

好的,我只是做了更多的测试。我试过这个:

let bodies = self.physicsBody.allContactedBodies() as? [SKPhysicsBody]

如果“allContactedBodies”为空,则转换成功。但是如果“allContactedBodies”包含对象,那么转换失败并且“body”将变为 nil,所以我不能循环遍历它。目前似乎不可能将 AnyObject 强制转换为 SKPhysicsBody,从而无法循环遍历“allContactedBodies”数组,除非有人可以提供解决方法。

编辑 3:Xcode 6 beta 5 中仍然存在错误。下面发布的解决方法仍然有效
编辑 4:Xcode 6 beta 6 中仍然存在错误。下面发布的解决方法仍然有效
编辑5:失望。 Xcode 6 GM 中仍然存在错误。下面发布的解决方法仍然有效

编辑 6:我收到了来自 Apple 的以下消息:

工程提供了以下信息:

我们相信这个问题已在最新的 Xcode 6.1 测试版中得到解决。

但事实并非如此,该错误仍在 Xcode 6.1.1 中!!!解决方法仍然有效。

编辑 7: Xcode 6.3,仍未修复,解决方法仍然有效。

【问题讨论】:

  • 我认为 SpriteKit 在 XCode6/ios stackoverflow.com/questions/24069168/… stackoverflow.com/questions/24222174/… 中无法正常工作是一个已知问题
  • 是的,尽管您提供的链接适用于 iOS 7。真正的问题似乎是无法将 AnyObject 转换为 SKPhysicsBody。除非可能有解决方法。
  • 尝试让body = object!作为 SKPhysicsBody
  • 这不起作用,因为 object 不是可选类型,所以你不能解开它。数组本身是一个隐式展开的可选类型,但数组中的 AnyObject 元素没有声明为可选类型。

标签: ios arrays casting swift sprite-kit


【解决方案1】:

经过反复试验,我找到了解决问题的解决方法。事实证明,当类型为 AnyObject 时,您根本不需要向下转换即可访问 SKPhysicsBody 的属性。

for object in self.physicsBody.allContactedBodies()  {
        if object.node??.name == "surface" {
            isOnSurface = true
        }
    }

【讨论】:

  • 感谢您发布更新。让我们希望在 Swift 1.0 和 iOS8 出来之前,向下转换的问题得到解决!尽管解决方法有效,但它完全违背了 Swift 的整个类型安全心态......
  • object.node??.name 行中的双问号到底有什么作用?我不确定我是否理解这里发生了什么。我知道这不是 nil 合并运算符。
  • @mogelbuster 好问题。这实际上是连续 2 级可选链接。尝试访问 AnyObject 上的属性将始终返回一个可选项,因为它可能会失败。而我们实际寻找的属性(名称)本身就是一个可选的!所以它实际上是可选中的可选。首先我们检查是否真的在这个对象上找到了 name 属性。那么接下来的检查就是我们对nil的正常检查,看名字是否为nil。
  • Xcode 7 更新: 此解决方案不再有效。 allContactedBodies() 的返回类型已更改为 [SKPhysicsBody],但您获得的基础类型仍然是 PKPhysicsBody。现在错误是NSArray element failed to match the Swift Array Element type 实际上必须将allContactedBodies() 的结果转换回[AnyObject],然后在迭代循环中使用unsafeBitCast 进行侧向转换。您可能仍然可以使用您的解决方法,但我无法让它工作。 ANSWER HERE
  • @mogelbuster:我看不到在 Xcode 7 / iOS 9 / OS X 10.11 中发生这种情况......像for body in physicsBody.allContactedBodies() { print(body.node) } 这样的东西工作得很好,就像使用这种模式的Apple sample code 一样.
【解决方案2】:

更新:这是一个错误,已在 iOS 9 / OS X 10.11 中修复。像下面这样的代码现在应该可以工作了:

for body in self.physicsBody.allContactedBodies()  {
    // inferred type body: SKPhysicsBody
    print(body.node) // call an API defined on SKPhysicsBody
}

为后代/使用旧 SDK 的人/等留下原始答案文本。


我在回答this one 时在相关问题侧边栏中注意到了这一点,事实证明这是同一个潜在问题。所以,虽然Epic Byte has a workable workaround,这里是问题的根源,为什么解决方法有效,以及更多的解决方法......

这并不是说您不能将AnyObject 转换为一般的SKPhysicsBody — 而是隐藏在这些特定AnyObject 引用后面的东西不能转换为SKPhysicsBody。

allContactedBodies() 返回的数组实际上包含PKPhysicsBody 对象,而不是SKPhysicsBody 对象。 PKPhysicsBody 不是公共 API — 大概,它应该是您看不到的实现细节。在 ObjC 中,将 PKPhysicsBody * 转换为 SKPhysicsBody * 非常酷……只要您只调用两个类碰巧共享的方法,它就会“正常工作”。但是在 Swift 中,您可以使用 as/as?/as! 仅向上或向下转换类型层次结构,并且 PKPhysicsBody 和 SKPhysicsBody 不是父类和子类。

你会得到一个错误转换let obj: AnyObject = SKPhysicsBody(); obj as SKPhysicsBody,因为即使是SKPhysicsBody 初始化程序也会返回PKPhysicsBody。大多数时候你不需要经历这个舞蹈(并且让它失败),因为你从一个声称返回一个 SKPhysicsBody 的初始化程序或方法中得到一个单一的 SKPhysicsBody ——所有的手波铸造SKPhysicsBody 和 PKPhysicsBody 之间发生在 ObjC 端,并且 Swift 信任导入的 ObjC API(并通过 ObjC 运行时回调原始 API,因此尽管类型不匹配,但它就像在 ObjC 中一样工作)。

但是当你转换整个数组时,需要在 Swift 端进行运行时类型转换,因此 Swift 更严格的类型检查规则开始发挥作用......将 PKPhysicsBody 实例转换为 SKPhysicsBody 失败了这些规则,所以你崩溃了。您可以将空数组强制转换为 [SKPhysicsBody] 而不会出错,因为数组中没有任何类型冲突的对象(数组中没有 any 对象)。

Epic Byte's workaround 之所以有效,是因为 Swift 的 AnyObject 与 ObjC 的 id 类型类似:编译器允许您在其上调用任何类的方法,并且您只希望在运行时处理的对象实际上实现了这些方法方法。

您可以通过显式强制进行侧面转换来恢复一点编译时类型安全:

for object in self.physicsBody.allContactedBodies() {
    let body = unsafeBitCast(object, SKPhysicsBody.self)
}

在此之后,body 是 SKPhysicsBody,因此编译器将只允许您在其上调用 SKPhysicsBody 方法...这类似于 ObjC 强制转换,因此您仍然希望您调用的方法实际上是由您正在与之交谈的对象实现的。但至少编译器可以帮助你保持诚实。 (你不能 unsafeBitCast 一个数组类型,所以你必须在循环内对元素进行处理。)

这应该被认为是一个错误,所以如果它影响到你,请let Apple know。

【讨论】:

  • 你能看看this question吗?我认为 OP 可以从您对SKPhysicsBody 和PKPhysicsBody 的深刻理解中受益。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-03
  • 1970-01-01
  • 1970-01-01
  • 2017-04-07
  • 1970-01-01
相关资源
最近更新 更多