properties - manager - Error en la clase Swift: Propiedad no inicializada en la llamada a super.init
swift guard try (11)
Agregue nil al final de la declaración.
// Must be nil or swift complains
var someProtocol:SomeProtocol? = nil
// Init the view
override init(frame: CGRect)
super.init(frame: frame)
...
Esto funcionó para mi caso, pero puede no funcionar para el tuyo
Tengo dos clases, Shape
y Square
class Shape {
var numberOfSides = 0
var name: String
init(name:String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with /(numberOfSides) sides."
}
}
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
super.init(name:name) // Error here
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Con la implementación anterior, recibo el error:
property ''self.sideLength'' not initialized at super.init call
super.init(name:name)
¿Por qué tengo que establecer self.sideLength
antes de llamar a super.init
?
Cita de The Swift Programming Language, que responde a su pregunta:
"El compilador de Swift realiza cuatro valiosas comprobaciones de seguridad para asegurarse de que la inicialización en dos fases se complete sin error:"
Comprobación de seguridad 1 "Un inicializador designado debe garantizar que todas las" propiedades introducidas por su clase se inicialicen antes de delegar en un inicializador de superclase ".
Extracto de: Apple Inc. "The Swift Programming Language". IBooks. https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11
De los docs
Verificación de seguridad 1
Un inicializador designado debe garantizar que todas las propiedades introducidas por su clase se inicialicen antes de delegar en un inicializador de superclase.
¿Por qué necesitamos un control de seguridad como este?
Para responder a esto, permite pasar rápidamente por el proceso de inicialización.
Inicialización bifásica
La inicialización de clase en Swift es un proceso de dos fases. En la primera fase, a cada propiedad almacenada se le asigna un valor inicial por parte de la clase que lo introdujo. Una vez que se ha determinado el estado inicial para cada propiedad almacenada, comienza la segunda fase y cada clase tiene la oportunidad de personalizar sus propiedades almacenadas aún más antes de que la nueva instancia se considere lista para su uso.
El uso de un proceso de inicialización de dos fases hace que la inicialización sea segura, a la vez que brinda una flexibilidad completa a cada clase en una jerarquía de clases. La inicialización en dos fases evita que se acceda a los valores de propiedad antes de que se inicialicen , y evita que los valores de propiedad se establezcan de forma inesperada en un valor diferente por otro inicializador.
Entonces, para asegurarse de que el proceso de inicialización de dos pasos se realiza como se define anteriormente, hay cuatro controles de seguridad, uno de ellos es,
Verificación de seguridad 1
Un inicializador designado debe garantizar que todas las propiedades introducidas por su clase se inicialicen antes de delegar en un inicializador de superclase.
Ahora, la inicialización de dos fases nunca habla de orden, pero esta verificación de seguridad, introduce super.init
que se ordenará, después de la inicialización de todas las propiedades.
La verificación de seguridad 1 puede parecer irrelevante ya que, la inicialización bifásica impide que se pueda acceder a los valores de propiedad antes de que se inicialicen , sin esta verificación de seguridad 1.
Como en esta muestra
class Shape {
var name: String
var sides : Int
init(sides:Int, named: String) {
self.sides = sides
self.name = named
}
}
class Triangle: Shape {
var hypotenuse: Int
init(hypotenuse:Int) {
super.init(sides: 3, named: "Triangle")
self.hypotenuse = hypotenuse
}
}
Triangle.init
ha inicializado, cada propiedad antes de ser utilizada. Entonces, el control de seguridad 1 parece irrelevante,
Pero luego podría haber otro escenario, un poco complejo,
class Shape {
var name: String
var sides : Int
init(sides:Int, named: String) {
self.sides = sides
self.name = named
printShapeDescription()
}
func printShapeDescription() {
print("Shape Name :/(self.name)")
print("Sides :/(self.sides)")
}
}
class Triangle: Shape {
var hypotenuse: Int
init(hypotenuse:Int) {
self.hypotenuse = hypotenuse
super.init(sides: 3, named: "Triangle")
}
override func printShapeDescription() {
super.printShapeDescription()
print("Hypotenuse :/(self.hypotenuse)")
}
}
let triangle = Triangle(hypotenuse: 12)
Salida:
Shape Name :Triangle
Sides :3
Hypotenuse :12
Aquí si hubiéramos llamado al super.init
antes de configurar la hypotenuse
, la llamada a super.init
habría llamado printShapeDescription()
y dado que se ha anulado, primero se recurriría a la implementación de la clase Triangle de printShapeDescription()
. printShapeDescription()
de la clase Triangle accede a la hypotenuse
una propiedad no opcional que aún no se ha inicializado. Y esto no está permitido, ya que la inicialización de dos fases evita que se acceda a los valores de propiedad antes de que se inicialicen
Así que asegúrese de que la inicialización de Dos fases se realiza como se define, debe haber un orden específico de llamadas super.init
, y es decir, después de inicializar todas las propiedades introducidas por self
clase self
, por lo tanto, necesitamos una verificación de seguridad 1
Eduardo,
Puede modificar el código en su ejemplo de esta manera:
var playerShip:PlayerShip!
var deltaPoint = CGPointZero
init(size: CGSize)
{
super.init(size: size)
playerLayerNode.addChild(playerShip)
}
Esto está usando una opción opcional implícitamente desenvuelta.
En la documentación podemos leer:
"Al igual que con los opcionales, si no proporciona un valor inicial cuando declara una propiedad o variable opcional implícitamente desenvuelta, su valor se establece automáticamente en cero".
Es un diseño asombrosamente estúpido.
Considera algo como esto:
.
.
.
var playerShip:PlayerShip
var deltaPoint = CGPointZero
init(size: CGSize)
{
super.init(size: size)
playerShip = PlayerShip(pos: CGPointMake(self.size.width / 2.0, 100))
playerLayerNode.addChild(playerShip)
}
.
.
.
Esto no es válido, como se indicó anteriormente. Pero también lo es:
.
.
.
var playerShip:PlayerShip = PlayerShip(pos: CGPointMake(self.size.width / 2.0, 100))
var deltaPoint = CGPointZero
init(size: CGSize)
{
super.init(size: size)
playerLayerNode.addChild(playerShip)
}
.
.
.
Porque ''yo'' no se ha inicializado.
Sinceramente espero que este error se resuelva pronto.
(Sí, sé que podría crear un objeto vacío y luego establecer el tamaño, pero eso es simplemente estúpido).
Perdón por el feo formato. Simplemente ponga un carácter de pregunta después de la declaración y todo estará bien. Una pregunta le dice al compilador que el valor es opcional.
class Square: Shape {
var sideLength: Double? // <=== like this ..
init(sideLength:Double, name:String) {
super.init(name:name) // Error here
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Edit1:
Hay una mejor manera de omitir este error. De acuerdo con el comentario de jmaschad, no hay ninguna razón para usar opcional en su caso porque los opcionales no son cómodos de usar y siempre debe verificar si opcional no es nulo antes de acceder a él. Entonces, todo lo que tiene que hacer es inicializar el miembro después de la declaración:
class Square: Shape {
var sideLength: Double=Double()
init(sideLength:Double, name:String) {
super.init(name:name)
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Edit2:
Después de dos negativos en esta respuesta, encontré una forma aún mejor. Si desea que el miembro de la clase se inicialice en su constructor, debe asignarle un valor inicial dentro de contructor y antes de la llamada a super.init (). Me gusta esto:
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
self.sideLength = sideLength // <= before super.init call..
super.init(name:name)
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Buena suerte para aprender Swift.
Se debe llamar al "super.init ()" después de inicializar todas sus variables de instancia.
En el video "Swift Intermedio" de Apple (puede encontrarlo en la página de recursos de video de desarrolladores de Apple https://developer.apple.com/videos/wwdc/2014/ ), alrededor de las 28:40, se dice explícitamente que todos los inicializadores en debe llamarse a la superclase DESPUÉS de que inicialice sus variables de instancia.
En Objective-C, fue al revés. En Swift, dado que todas las propiedades deben inicializarse antes de su uso, primero debemos inicializar las propiedades. Esto está destinado a evitar una llamada a la función anulada desde el método "init ()" de la superclase, sin inicializar primero las propiedades.
Entonces, la implementación de "Square" debería ser:
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
self.sideLength = sideLength
numberOfSides = 4
super.init(name:name) // Correct position for "super.init()"
}
func area () -> Double {
return sideLength * sideLength
}
}
Solo estás ingresando en el orden incorrecto.
class Shape2 {
var numberOfSides = 0
var name: String
init(name:String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with /(numberOfSides) sides."
}
}
class Square2: Shape2 {
var sideLength: Double
init(sideLength:Double, name:String) {
self.sideLength = sideLength
super.init(name:name) // It should be behind "self.sideLength = sideLength"
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Swift no le permitirá inicializar la súper clase sin inicializar las propiedades, al revés de Obj C. Entonces, debe inicializar todas las propiedades antes de llamar a "super.init".
Vaya a http://blog.scottlogic.com/2014/11/20/swift-initialisation.html . Da una buena explicación a tu problema.
Swift tiene una secuencia de operaciones muy clara y específica que se realiza en inicializadores. Comencemos con algunos ejemplos básicos y trabajemos hasta llegar a un caso general.
Tomemos un objeto A. Lo definiremos de la siguiente manera.
class A {
var x: Int
init(x: Int) {
self.x = x
}
}
Observe que A no tiene una superclase, por lo que no puede llamar a una función super.init () ya que no existe.
OK, así que ahora vamos a la subclase A con una nueva clase llamada B.
class B: A {
var y: Int
init(x: Int, y: Int) {
self.y = y
super.init(x: x)
}
}
Esto es una desviación de Objective-C donde [super init]
normalmente se llamaría primero antes que cualquier otra cosa. No es así en Swift. Usted es responsable de asegurarse de que sus variables de instancia estén en un estado constante antes de hacer cualquier otra cosa, incluidos los métodos de llamada (que incluyen el inicializador de su superclase).
swift lo obliga a inicializar cada var miembro antes de poder usarlo alguna vez. Como no puede estar seguro de lo que sucede cuando está súper turno, se equivoca: es mejor prevenir que lamentar