iPhone: ¿es initWithCoder una excepción al patrón de diseño de inicializador designado habitual?
initialization archiving (1)
Las manzanas dicen que:
inicializador designado El método init ... que tiene la responsabilidad principal de inicializar nuevas instancias de una clase. Cada clase define o hereda su propio inicializador designado. A través de mensajes a sí mismo, otros métodos init ... en la misma clase invocan directa o indirectamente el inicializador designado, y el inicializador designado, a través de un mensaje a super, invoca el inicializador designado de su superclase. [emp agregó]
En principio, el inicializador designado es el único método de inicio al que llaman todos los demás métodos de inicio. No es, sin embargo, el único método init. Tampoco cada clase tiene que tener la suya propia. Más a menudo, en la práctica, el inicializador designado es en realidad la súper clase ''init.
La función principal de initWithCoder
es permitir la inicialización de un objeto archivado. En el caso de una clase que requiere algunos datos específicos, su inicializador designado aceptará esos datos. initWithCoder
luego simplemente desempaqueta el archivo y luego llama al inicializador designado.
Por ejemplo, el inicializador designado para UIView es initWithFrame:
Entonces, el initWithCoder de initWithCoder
ve algo así como:
- (id)initWithCoder:(NSCoder *)decoder{
CGRect theFrame= //...uppack frame data
self=[self initWithFrame:theFrame];
return self;
}
El punto del inicializador designado es crear un punto central por el que deba pasar toda la inicialización para garantizar que cada instancia se inicialice por completo, independientemente de la procedencia de los datos o las circunstancias de la inicialización.
Nunca se debe considerar que una clase solo puede tener un método de inicialización.
Edit01
De los comentarios:
En particular, ¿cómo paso los valores de algunos de mis ivars cuando la inicialización se realiza a través de initWithCoder?
Bueno tu no El punto entero de initWithCoder es que está tratando con una instancia de su clase que no contiene datos congelados que contiene todos los datos necesarios para recrear el objeto.
El protocolo NSCoding hace que tu clase se comporte como el camarón en salmuera que venden como "Sea Monkeys" en los cómics. Los métodos de codificación deshidratan / congelan y secan la salmuera-camarones / instancias. Los métodos de decodificación hidratan la salmuera-camarones / instancias al igual que verter la salmuera de camarones en el agua. Al igual que los camarones en salmuera tienen todo lo que necesitan para comenzar a vivir, excepto el agua, un objeto codificado guardado en el disco tiene todos los datos necesarios para recrearse una vez que se haya inicializado con el codificador.
El ejemplo canónico de esto es un archivo de plumilla. Un archivo de plumilla es solo un montón de instancias de elementos y controladores de IU liofilizados. Un UIViewController y sus UIViews en una plumilla tienen todos los datos que necesitan para inicializarse codificados en el xml del archivo de plumilla. Cuando llama a initFromNib
directamente o con un IBOutlet, llama a cada clase '' intiWithCoder:
método.
Si no guarda el objeto completo cuando lo seca, los atributos que no se liofilizan no son necesarios para que el objeto de instancia exista.
Solo establece esos atributos auxiliares una vez que el objeto ha sido inicializado.
Para alinear el inicializador designado, primero debe decodificar y luego llamar al inicializador designado. Al igual que:
-(id) initWithRequiredValue:(id) someValue otherRequiredValue:(id) anotherValue{
if (self=[super init]){
self.requiredProperty=someValue;
self.anotherRequiredProperty=anotherValue
}
return self;
}
Si la superclase no es compatible con NSCoder, entonces usted mismo lo inicia en la subclase:
- (id)initWithCoder:(NSCoder *)decoder {
id someDecodedValue=[decoder decodeObjectForKey:@"someValueKey"];
id someOtherDecodedValue=[decoder decodeObjectForKey:@"someOtherValueKey"];
self=[self initWithRequiredValue:someDecodedValue otherRequiredValue:someOtherDecodedValue];
return self;
}
Ese es el caso más simple. Si el super en sí mismo es compatible con NSCoding, entonces normalmente terminas escribiendo un inicializador designado paralelo como:
- (id)initWithCoder:(NSCoder *)decoder {
if (self=[super initWithCoder:decoder]){
id someDecodedValue=[decoder decodeObjectForKey:@"someValueKey"];
id someOtherDecodedValue=[decoder decodeObjectForKey:@"someOtherValueKey"];
self.requiredProperty=someDecodedValue;
self.anotherRequiredProperty=someOtherDecodedValue;
}
return self;
}
Creo que en la mayoría de los casos, initWithCoder
termina siendo un inicializador designado en paralelo porque se encarga de toda la inicialización como debe hacerlo el inicializador designado. No se parece al inicializador designado porque todos los datos son proporcionados por el codificador pero realiza la misma función.
Este es uno de esos casos donde la teoría y la práctica no se alinean bien. El concepto de "inicializador designado" realmente solo se aplica a los casos en los que crea instancias desde cero.
Tengo una clase MyClass. Tiene variables de instancia pasadasInVar1, pasadasInVar2, etc. cuyos valores se pasarán desde el objeto que solicita la inicialización. También tiene variables de instancia decodedVar1, decodedVar2, etc. que se descodificarán de un archivo, o se establecerán en un valor predeterminado si no hay ningún archivo.
Según Apple ,
Cuando un objeto recibe un mensaje initWithCoder: el objeto debe enviar primero un mensaje a su superclase (si corresponde) para inicializar las variables de instancia heredadas, y luego debe decodificar e inicializar sus propias variables de instancia.
Pero Apple también dice que una clase debe tener un inicializador designado único.
¿Cuál es la mejor manera de lidiar con todo esto?