Tengo dos clases Shape
ySquare
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 obtengo el error:
property 'self.sideLength' not initialized at super.init call
super.init(name:name)
¿Por qué tengo que configurar self.sideLength
antes de llamar super.init
?
properties
compiler-errors
swift
JuJoDi
fuente
fuente
Respuestas:
Cita del lenguaje de programación Swift, que responde a su pregunta:
fuente
init
.Swift tiene una secuencia muy clara y específica de operaciones que se realizan en inicializadores. Comencemos con algunos ejemplos básicos y avancemos hasta llegar a un caso general.
Tomemos un objeto A. Lo definiremos de la siguiente manera.
Observe que A no tiene una superclase, por lo que no puede llamar a una función super.init () ya que no existe.
Bien, ahora subclasemos A con una nueva clase llamada B.
Esta 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 garantizar que sus variables de instancia estén en un estado coherente antes de hacer cualquier otra cosa, incluidos los métodos de llamada (que incluye el inicializador de su superclase).fuente
De los documentos
¿Por qué necesitamos un control de seguridad como este?
Para responder esto, avancemos rápidamente el proceso de inicialización.
Entonces, para asegurarse de que el proceso de inicialización de dos pasos se realice como se definió anteriormente, hay cuatro controles de seguridad, uno de ellos es,
Ahora, la inicialización de dos fases nunca habla del orden, pero este control de seguridad, se introduce
super.init
para ser ordenado, después de la inicialización de todas las propiedades.La comprobación de seguridad 1 puede parecer irrelevante ya que, la inicialización de dos fases evita que se pueda acceder a los valores de propiedad antes de que puedan inicializarse , sin esta comprobación de seguridad 1.
Como en esta muestra
Triangle.init
ha inicializado, cada propiedad antes de ser utilizada. Entonces el control de seguridad 1 parece irrelevante,Pero entonces podría haber otro escenario, un poco complejo,
Salida:
Aquí, si hubiéramos llamado al
super.init
antes de establecer elhypotenuse
, lasuper.init
llamada habría llamado alprintShapeDescription()
y, dado que se ha anulado, primero recurriría a la implementación de la clase TriangleprintShapeDescription()
. LaprintShapeDescription()
clase Triangle accede ahypotenuse
una propiedad no opcional que aún no se ha inicializado. Y esto no está permitido ya que la inicialización en dos fases evita que se acceda a los valores de propiedad antes de que se inicialicenPor lo tanto, asegúrese de que la inicialización de dos fases se realice según lo definido, debe haber un orden específico de llamada
super.init
, y es decir, después de inicializar todas las propiedades introducidas por laself
clase, por lo tanto, necesitamos un control de seguridad 1fuente
init
pueden llamar a una función (anulada) ... en la que esa función accede a la propiedad de subclases, para evitar no tener valores establecidos, la llamadasuper
debe ocurrir después de que todos los valores estén establecidos. OK tiene sentido. ¿Se pregunta cómo lo hizo Objective-C entonces y por qué tuvo que llamarsuper
primero?printShapeDescription()
antes deself.sides = sides; self.name = named;
lo que generaría este error:use of 'self' in method call 'printShapeDescription' before all stored properties are initialized
. El error del OP se da para reducir la 'posibilidad' de error de tiempo de ejecución.printShapeDescription
fuera una función a la que no se refería,self
es decir, sería algo como 'print ("nada"), entonces no habría problemas. (Sin embargo, incluso para eso, el compilador arrojaría un error, porque no es súper inteligente)Se debe llamar a "super.init ()" después de inicializar todas las variables de instancia.
En el video "Intermediate Swift" de Apple (puede encontrarlo en la página de recursos de video para desarrolladores de Apple https://developer.apple.com/videos/wwdc/2014/ ), aproximadamente a las 28:40, se dice explícitamente que todos los inicializadores en la superclase debe llamarse DESPUÉS de inicializar sus variables de instancia.
En Objective-C, fue al revés. En Swift, dado que todas las propiedades deben inicializarse antes de usarse, primero debemos inicializar las propiedades. Esto está destinado a evitar una llamada a la función anulada del método "init ()" de la superclase, sin inicializar primero las propiedades.
Entonces la implementación de "Square" debería ser:
fuente
Perdón por el formato feo. 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.
Editar1:
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 las opcionales no son cómodas y siempre debe verificar si la opción no es nula antes de acceder a ella. Entonces, todo lo que tiene que hacer es inicializar el miembro después de la declaración:
Edit2:
Después de dos inconvenientes 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 del contructor y antes de la llamada super.init (). Me gusta esto:
Buena suerte en aprender Swift.
fuente
super.init(name:name)
yself.sideLength = sideLength
. DeclararsideLength
que es opcional es erróneo e introduce problemas adicionales más adelante cuando tiene que forzar el desenvolvimiento.var sideLength: Double
, no es necesario asignarle un valor inicialswift le obliga a inicializar cada miembro var antes de que se use / pueda usarse. Dado que no puede estar seguro de lo que sucede cuando es super giro, se equivoca: más vale prevenir que curar
fuente
super.init
en la primera línea?Eduardo,
Puede modificar el código en su ejemplo así:
Esto está utilizando un opcional implícitamente sin envolver.
En la documentación podemos leer:
fuente
Swift no le permitirá inicializar la superclase sin inicializar las propiedades, al revés de Obj C. Por lo tanto, 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 su problema.
fuente
Agregue nulo al final de la declaración.
Esto funcionó para mi caso, pero puede no funcionar para el suyo
fuente
Simplemente estás iniciando en el orden incorrecto.
fuente
Probablemente recibiré algunos votos negativos, pero para ser sincero, la vida es más fácil de esta manera:
fuente
Es un diseño asombrosamente estúpido.
Considera algo como esto:
Esto no es válido, como se señaló anteriormente. Pero también lo es:
Porque 'yo' no se ha inicializado.
Sinceramente espero que este error se solucione pronto.
(Sí, sé que podría crear un objeto vacío y luego establecer el tamaño, pero eso es simplemente estúpido).
fuente