问题
The following code gives an error - it appears the physics joints array have the class PKPhysicsJoint. Anyone have any ideas how I can iterate through the joints in Swift?
The documentation does say that physicsBody.joints should return an array of SKPhysicsJoint.
import SpriteKit
let scene = SKScene(size: CGSize(width: 200, height: 200))
let nodeA = SKNode()
let nodeB = SKNode()
nodeA.physicsBody = SKPhysicsBody(circleOfRadius: 20)
nodeB.physicsBody = SKPhysicsBody(circleOfRadius: 20)
scene.addChild(nodeA)
scene.addChild(nodeB)
let joint = SKPhysicsJointFixed.jointWithBodyA(nodeA.physicsBody, bodyB: nodeB.physicsBody, anchor: CGPointZero)
scene.physicsWorld.addJoint(joint)
for joint in nodeA.physicsBody!.joints as [SKPhysicsJoint] {
// do something else here
}
gives error:
Execution was interrupted. reason: EXC_BAD_INSTRUCTION...
回答1:
Update: This was a bug, and it's fixed in iOS 9 / OS X 10.11 — the code in the question just works now.
Leaving original answer text for posterity / folks using older SDKs / etc.
This looks like a bug — you should file it. Whether it should be considered a SpriteKit bug or a Swift bug is hard to say, but that's Apple's problem, not yours. :)
The problem is clear if you paste your code into a playground — your joint
is actually a PKPhysicsJointWeld
behind the scenes. That's some internal class that should be an implementation detail. In ObjC that's no problem, because casting in C is just a matter of telling the compiler, "trust me, this pointer is really an SKPhysicsJoint
, so let me call physics joint methods (and nothing else) on it and and no one will be the wiser". Casting in Swift requires that there be a type hierarchy relationship between the casted types — and PKPhysicsJointWeld
is not a subtype/subclass of SKPhysicsJoint
, so the cast fails.
You can work around this issue by avoiding the cast to [SKPhysicsJoint]
:
for joint in nodeA.physicsBody!.joints {
// do something else here
}
With this, you lose some type safety — joint
is an AnyObject
, so like ObjC's id
type the compiler lets you call any method on it. (And it may fail at runtime if that object doesn't implement the method.) But at least it runs.
A further workaround: inside the loop, you can cast joint
to SKPhysicsJoint
. But since that cast is across the type hierarchy, you have to use unsafeBitCast
:
for joint in nodeA.physicsBody!.joints {
let skJoint = unsafeBitCast(joint, SKPhysicsJoint.self)
// do stuff with skJoint
}
This gets you back a little bit of compile-time type "safety", in that thereafter the compiler will require anything you do with skJoint
to be compatible with SKPhysicsJoint
, but it's still inherently unsafe in that it depends on some hand-waving around runtime types that may not always hold. And you have to unsafeBitCast
again to get to a particular joint subclass, without knowing which subclass it might be. (Again, this would be a good time to file a bug.)
(You might notice from pasting into a playground that physicsWorld
is of an internal class, too: PKPhysicsWorld
. So why doesn't that fail, too? When you use the physicsWorld
property, all the type casting happens on the ObjC side, and Swift trusts whatever ObjC tells it. When you deal with the joints
array, though, you have to do a type cast on the Swift side, and Swift is much more strict about type checking.)
来源:https://stackoverflow.com/questions/28501832/node-physicsbody-joints-downcasting-error