¿Qué es exactamente init coder aDecoder?

122

Estoy aprendiendo el desarrollo de iOS a partir de un curso en línea y cada vez que hago una vista personalizada (celda de vista de tabla personalizada, celda de vista de colección, etc.) el instructor siempre implementa este inicializador:

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

¿Por qué exactamente siempre tengo que llamar a esto? ¿Qué hace? ¿Puedo poner propiedades dentro del init?

JasonP
fuente
5
Esta respuesta lo ayudará a stackoverflow.com/questions/24036393/… Gracias
Seungyoun Yi
2
Si subclasifica un objeto que implementa, NSCodingentonces necesita implementar este inicializador, ya que se requiere de las clases que implementan NSCoding. Debe llamar al menos al método init de la superclase. Si NSCodercontiene propiedades codificadas para su clase, puede usar este método para recuperarlas
Paulw11
1
Además, te recomiendo que leas la sección sobre inicialización de objetos en el libro oficial Swift de Apple.
Nicolas Miari

Respuestas:

121

Comenzaré esta respuesta desde la dirección opuesta: ¿qué pasa si desea guardar el estado de su vista en el disco? Esto se conoce como serialización . Lo contrario es la deserialización : restaurar el estado del objeto desde el disco.

El NSCodingprotocolo define dos métodos para serializar y deserializar objetos:

encodeWithCoder(_ aCoder: NSCoder) {
    // Serialize your object here
}

init(coder aDecoder: NSCoder) {
    // Deserialize your object here
}

Entonces, ¿por qué es necesario en tu clase personalizada? La respuesta es Interface Builder. Cuando arrastra un objeto a un guión gráfico y lo configura, Interface Builder serializa el estado de ese objeto en el disco y luego lo deserializa cuando el guión gráfico aparece en la pantalla. Necesita decirle a Interface Builder cómo hacerlo. Como mínimo, si no agrega ninguna propiedad nueva a su subclase, simplemente puede pedirle a la superclase que haga el empaque y desempaque por usted, de ahí elsuper.init(coder: aDecoder) llamada. Si su subclase es más compleja, debe agregar su propio código de serialización y deserialización para la subclase.

Esto contrasta con el enfoque de Visual Studio, que consiste en escribir código en un archivo oculto para crear el objeto en tiempo de ejecución.

Código diferente
fuente
¿Por qué no poner todo dentro de awakeFromNib y olvidarse de usarlo init(coder aCoder : NSCoder)?
Cariño
@Honey - en una palabra, "a veces no puedes hacer eso". Normalmente puede, pero no siempre.
Fattie
@Fattie, ¿son demasiado complejos o innecesarios los detalles de no hacerlo? Si no, ¿te importaría explicar?
Cariño
9
@Honey, si desea configurar su objeto en Interface Builder awakeFromNib, no funcionará. awakeFromNibse invoca en tiempo de ejecución . Todo lo que haga en Interface Builder es durante el tiempo de diseño . Para llevar lo que ha hecho en tiempo de diseño al tiempo de ejecución es encodeWithCoder(guardar) y init(coder:)(cargar)
Código diferente
3
@Honey si no usa Interface Builder para configurar su clase personalizada (es decir, hágalo programáticamente con código), entonces puede hacerlo en awakeFromNiboinitWIthFrame
Code Different
28

El requisito de implementar ese inicializador es una consecuencia de dos cosas:

  1. El principio de sustitución de Liskov . Si S es una subclase de T (por ejemplo, MyViewControlleres una subclase de ViewController), entonces los objetos S (instancias de MyViewController) deben poder sustituirse donde ViewControllerse esperan objetos T (instancias de ).

  2. Los inicializadores no se heredan en Swift si algún inicializador se define explícitamente en la subclase. Si se proporciona explícitamente un inicializador, todos los demás deben proporcionarse explícitamente (que luego puede simplemente llamar super.init(...)). Consulte esta pregunta para conocer la justificación. Está en Java, pero todavía se aplica.

En el punto 1, todo lo que ViewControllerpuede hacer el original ,MyViewController subclase debería poder hacerlo. Una de esas cosas es poder inicializarse a partir de un determinado NSCoder. En el punto 2, su MyViewControllersubclase no hereda automáticamente esta habilidad. Por lo tanto, debe proporcionar manualmente el inicializador que cumpla con este requisito. En este caso, solo necesita delegar hasta la superclase, para que haga lo que normalmente haría.

Alexander - Reincorporar a Monica
fuente
1
Tiene mucho sentido que los constructores no se hereden: si inicializa una instancia de la clase derivada usando el inicializador (heredado) de la clase base, las propiedades no heredadas que fueron recientemente definidas ("agregadas") por la clase derivada nunca ser inicializado.
Nicolas Miari
3
En realidad, los inicializadores se heredan en Swift, dado que no proporciona ninguna de sus propias implementaciones de inicializador en su subclase. Si sus propiedades no heredadas recién definidas tienen valores predeterminados, puede salirse con la suya sin escribir ningún inicializador en su subclase y simplemente heredar todos los inicializadores de su superclase. Ver aqui
TheBaj