tutorial started kits getting example create ios cocoa sprite-kit swift

ios - started - tutorial swift



AdiciĆ³n de inicializadores de conveniencia en Swift Subclass (3)

Hay dos problemas aquí:

  • SKShapeNode tiene un solo inicializador designado: init() . Esto significa que no podemos salir de nuestro inicializador sin llamar a init() .

  • SKShapeNode tiene una path propiedad declarada como CGPath! . Esto significa que no queremos salir de nuestro inicializador sin inicializar de alguna manera la path .

La combinación de estas dos cosas es la fuente del problema. En pocas palabras, SKShapeNode está incorrectamente escrito. Tiene una path propiedad que debe ser inicializada; por lo tanto, debe tener un inicializador designado que establezca la path . Pero no es así (todos sus inicializadores de configuración de path son inicializadores de conveniencia). Ese es el error. Dicho de otra manera, la fuente del problema es que, convenientemente o no, los métodos shapeNodeWith... no son realmente inicializadores en absoluto.

Sin embargo, puede hacer lo que quiera hacer: escribir un inicializador de conveniencia sin verse obligado a escribir otros inicializadores, cumpliendo ambos requisitos en ese orden , es decir, escribiéndolo así:

class MyShapeNode : SKShapeNode { convenience init(squareOfSize value: CGFloat) { self.init() self.init(rectOfSize: CGSizeMake(value, value)) } }

Parece ilegal, pero no lo es. Una vez que hemos llamado self.init() , hemos cumplido con el primer requisito, y ahora somos libres de referirnos a self (ya no obtenemos el "uso de ''self'' al delegar el inicializador antes de que self.init se llame" error) y satisfacer el segundo requisito.

Como ejercicio de aprendizaje, estoy tratando de implementar una subclase de SKShapeNode que proporciona un nuevo inicializador de conveniencia que toma un número y construye un ShapeNode que es un cuadrado de ancho y alto de un número.

Según el libro Swift :

Regla 1

Si su subclase no define ningún inicializador designado, automáticamente hereda todos sus inicializadores designados superclase.

Regla 2

Si su subclase proporciona una implementación de todos sus inicializadores designados de superclase, ya sea heredándolos según la regla 1, o proporcionando una implementación personalizada como parte de su definición, entonces heredará automáticamente todos los inicializadores de conveniencia de superclase ".

Sin embargo, la siguiente clase no funciona:

class MyShapeNode : SKShapeNode { convenience init(squareOfSize value: CGFloat) { self.init(rectOfSize: CGSizeMake(value, value)) } }

En su lugar me sale:

Playground execution failed: error: <REPL>:34:9: error: use of ''self'' in delegating initializer before self.init is called self.init(rectOfSize: CGSizeMake(value, value)) ^ <REPL>:34:14: error: use of ''self'' in delegating initializer before self.init is called self.init(rectOfSize: CGSizeMake(value, value)) ^ <REPL>:35:5: error: self.init isn''t called on all paths in delegating initializer }

Entiendo que MyShapeNode debería heredar todos los inicializadores convenientes de SKShapeNode porque no estoy implementando ninguno de mis propios inicializadores designados, y porque mi inicializador de conveniencia está llamando a init(rectOfSize) , otro inicializador de conveniencia, esto debería funcionar. ¿Qué estoy haciendo mal?


Mi comprensión de la herencia del Inicializador es la misma que la suya, y creo que ambos estamos bien alineados con lo que dice el libro. No creo que sea un problema de interpretación o una mala interpretación de las reglas establecidas. Dicho esto, no creo que estés haciendo nada mal.

Probé lo siguiente en un patio de recreo y funciona como se esperaba:

class RectShape: NSObject { var size = CGSize(width: 0, height: 0) convenience init(rectOfSize size: CGSize) { self.init() self.size = size } } class SquareShape: RectShape { convenience init(squareOfSize size: CGFloat) { self.init(rectOfSize: CGSize(width: size, height: size)) } }

RectShape hereda de NSObject y no define ningún inicializador designado. Por lo tanto, según la Regla 1, hereda todos los inicializadores designados de NSObject . El inicializador de conveniencia que proporcioné en la implementación delega correctamente a un inicializador designado, antes de realizar la configuración de la intención.

SquareShape hereda de RectShape , no proporciona un inicializador designado y, nuevamente, según la Regla 1, hereda todos los inicializadores designados de SquareShape . Según la Regla 2, también hereda el inicializador de conveniencia definido en RectShape . Finalmente, el inicializador de conveniencia definido en SquareShape delega adecuadamente al inicializador de conveniencia heredado, que a su vez delega el inicializador designado heredado.

Entonces, dado que no estás haciendo nada malo y que mi ejemplo funciona como se esperaba, estoy extrapolando la siguiente hipótesis:

Dado que SKShapeNode está escrito en Objective-C, la regla no establece que "todo inicializador de conveniencia debe llamar a otro inicializador de la misma clase" no se aplica por el idioma. Por lo tanto, tal vez el inicializador de conveniencia para SKShapeNode realidad no llame a un inicializador designado. Por lo tanto, aunque la subclase MyShapeNode hereda los inicializadores de conveniencia como se esperaba, no delegan adecuadamente al inicializador designado heredado.

Pero, de nuevo, es sólo una hipótesis. Todo lo que puedo confirmar es que la mecánica funciona como se esperaba en las dos clases que yo mismo creé.


Sobre la base de la respuesta de Matt, tuvimos que incluir una función adicional, o el compilador se quejó de invocar un inicializador sin argumentos.

Esto es lo que funcionó en la subclase SKShapeNode:

class CircleNode : SKShapeNode { override init() { super.init() } convenience init(width: CGFloat, point: CGPoint) { self.init() self.init(circleOfRadius: width/2) // Do stuff } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }