Rápido:No se puede abatir AnyObject a SKPhysicsBody
-
20-12-2019 - |
Pregunta
Apple tiene el siguiente método en la clase SKPhysicsBody.
/* Returns an array of all SKPhysicsBodies currently in contact with this one */
func allContactedBodies() -> [AnyObject]!
Noté que devuelve una matriz de AnyObject.Entonces leí sobre cómo lidiar con el lanzamiento de AnyObject. Aquí
Quiero recorrer la matriz allContactedBodies de mi cuerpo físico.El problema es que no importa lo que intento, simplemente no puedo hacer que las cosas funcionen.
Probé esto primero:
for body in self.physicsBody.allContactedBodies() as [SKPhysicsBody] {
}
Pero me sale este error.
error fatal:la matriz no se puede reducir a una matriz de derivados
También probé esto:
for object in self.physicsBody.allContactedBodies() {
let body = object as SKPhysicsBody
}
Pero esto también falla con lo siguiente:
Y de manera similar probé esto:
for object in self.physicsBody.allContactedBodies() {
let body = object as? SKPhysicsBody
}
No hay choque, pero el "cuerpo" se vuelve nulo.
Y si no lanzo nada, no me bloqueo.Por ejemplo:
for object in self.physicsBody.allContactedBodies() {
}
Pero obviamente necesito emitir si quiero usar el tipo real.
Entonces, como prueba, probé esto:
let object: AnyObject = SKPhysicsBody()
let body = object as SKPhysicsBody
Y esto también resulta en el mismo bloqueo que se muestra en la imagen.
Pero otros tipos no fallarán.Por ejemplo, esto no fallará.
let object: AnyObject = SKNode()
let node = object as SKNode
Entonces mi pregunta es, ¿Cómo puedo recorrer correctamente la matriz allContactedBodies??
Editar:Estoy ejecutando Xcode 6 beta 4 en un dispositivo iOS 8 beta 4.
Edición 2:Más información
Ok, acabo de hacer algunas pruebas más.Probé esto:
let bodies = self.physicsBody.allContactedBodies() as? [SKPhysicsBody]
Si "allContactedBodies" está vacío, entonces la transmisión se realizó correctamente.Pero si "allContactedBodies" contiene objetos, entonces la conversión falla y "bodies" se volverá nulo, por lo que no puedo recorrerlo.Parece que actualmente NO ES POSIBLE enviar AnyObject a SKPhysicsBody, lo que hace imposible recorrer la matriz "allContactedBodies", a menos que alguien pueda proporcionar una solución alternativa.
Edición 3:Error aún en Xcode 6 beta 5.La solución alternativa publicada a continuación todavía funciona
Edición 4:Error aún en Xcode 6 beta 6.La solución alternativa publicada a continuación todavía funciona
Edición 5:Decepcionado.Error todavía en Xcode 6 GM.La solución alternativa publicada a continuación todavía funciona
EDITAR 6:He recibido el siguiente mensaje de Apple:
Ingeniería ha proporcionado la siguiente información:
Creemos que este problema se solucionó en la última versión beta de Xcode 6.1.
¡PERO NO LO ES, el error todavía está en Xcode 6.1.1!La solución todavía funciona.
Edición 7: Xcode 6.3, aún no solucionado, la solución alternativa aún funciona.
Solución
Después de una gran prueba y error, he encontrado una workarlound a mi problema.Resulta que no necesita opcional para acceder a las propiedades del cuerpo skphysics, cuando el tipo es anyObject.
for object in self.physicsBody.allContactedBodies() {
if object.node??.name == "surface" {
isOnSurface = true
}
}
Otros consejos
Actualizar:Esto fue un error y se solucionó en iOS 9/OS X 10.11.Código como el siguiente debería funcionar ahora:
for body in self.physicsBody.allContactedBodies() {
// inferred type body: SKPhysicsBody
print(body.node) // call an API defined on SKPhysicsBody
}
Dejando el texto de respuesta original para la posteridad/personas que usan SDK más antiguos/etc.
Noté esto en la barra lateral de preguntas relacionadas mientras respondía Éste, y resulta ser el mismo problema subyacente.Entonces, mientras Epic Byte tiene una solución viable, aquí está la raíz del problema, por qué funciona la solución y algunas soluciones más...
No es que no puedas lanzar AnyObject
a SKPhysicsBody
en general, es que las cosas que se esconden detrás de estos AnyObject
no se pueden enviar referencias a SKPhysicsBody
.
La matriz devuelta por allContactedBodies()
en realidad contiene PKPhysicsBody
objetos, no SKPhysicsBody
objetos. PKPhysicsBody
no es una API pública; presumiblemente, se supone que es un detalle de implementación que no se ve.En ObjC, es genial lanzar un PKPhysicsBody *
a SKPhysicsBody *
..."simplemente funcionará" siempre que llame sólo a métodos que las dos clases comparten.Pero en Swift, puedes transmitir con as
/as?
/as!
sólo hacia arriba o hacia abajo en la jerarquía de tipos, y PKPhysicsBody
y SKPhysicsBody
no son una clase padre ni una subclase.
Obtienes un error al transmitir let obj: AnyObject = SKPhysicsBody(); obj as SKPhysicsBody
porque incluso el SKPhysicsBody
El inicializador está devolviendo un PKPhysicsBody
.La mayoría de las veces no necesitas pasar por este baile (y que falle), porque obtienes un solo SKPhysicsBody
desde un inicializador o método que pretende devolver un SKPhysicsBody
— todo el casting ondulado a mano entre SKPhysicsBody
y PKPhysicsBody
está sucediendo en el lado de ObjC, y Swift confía en la API de ObjC importada (y vuelve a llamar a la API original a través del tiempo de ejecución de ObjC, por lo que funciona tal como lo haría en ObjC a pesar de la discrepancia de tipos).
Pero cuando lanzas una matriz completa, es necesario que se produzca un encasillado en tiempo de ejecución en el lado de Swift, por lo que entran en juego las reglas más estrictas de verificación de tipos de Swift...lanzando un PKPhysicsBody
instancia a SKPhysicsBody
falla esas reglas, entonces te estrella.Puedes lanzar una matriz vacía para [SKPhysicsBody]
sin error porque no hay ningún objeto de tipo conflictivo en la matriz (no hay cualquier objetos en la matriz).
La solución alternativa de Epic Byte funciona porque Swift AnyObject
funciona como el de ObjC id
tipo:el compilador te permite llamar a métodos de cualquier clase, y solo esperas que en tiempo de ejecución estés tratando con un objeto que realmente implemente esos métodos.
Puedes recuperar un poco de seguridad de tipos en tiempo de compilación forzando explícitamente una conversión lateral:
for object in self.physicsBody.allContactedBodies() {
let body = unsafeBitCast(object, SKPhysicsBody.self)
}
Después de este, body
es un SKPhysicsBody
, por lo que el compilador le permitirá llamar sólo SKPhysicsBody
métodos al respecto...esto se comporta como una conversión de ObjC, por lo que todavía tienes la esperanza de que los métodos que llamas realmente sean implementados por el objeto con el que estás hablando.Pero al menos el compilador puede ayudarle a ser honesto.(No puedes unsafeBitCast
un tipo de matriz, por lo que debe hacerlo con el elemento, dentro del bucle).
Probablemente esto debería considerarse un error, así que por favor hazle saber a Apple si te está afectando.