objective-c null init

objective c - En Objective-C, ¿por qué debería verificar si self=[super init] no es nulo?



null (9)

Tengo una pregunta general sobre cómo escribir métodos init en Objective-C.

Lo veo en todas partes (código de Apple, libros, código fuente abierto, etc.) que un método init debería verificar si self = [super init] no es nulo antes de continuar con la inicialización.

La plantilla predeterminada de Apple para un método init es:

- (id) init { self = [super init]; if (self != nil) { // your code here } return self; }

¿Por qué?

Quiero decir, ¿cuándo init va a volver a cero? Si llamé a init en NSObject y obtuve cero, entonces algo debe estar realmente jodido, ¿no? Y en ese caso, es mejor que ni siquiera escribas un programa ...

¿Es realmente tan común que un método init de clase puede devolver nada? Si es así, ¿en qué caso y por qué?


Creo que, en la mayoría de las clases, si el valor de retorno de [superinicio] es nulo y lo compruebas, como recomiendan las prácticas estándar, y luego regresas prematuramente si es nulo, básicamente tu aplicación todavía no funcionará correctamente. Si lo piensas bien, aunque eso sí (¡sí! = Nil) esté ahí, para el correcto funcionamiento de tu clase, el 99,99% de las veces realmente necesitas que el yo sea no-nil. Supongamos ahora que, por alguna razón, [superinicial] devolvió nulo, básicamente su cheque contra cero básicamente le está pasando la pelota a la persona que llama de su clase, donde probablemente fallaría de todos modos, ya que naturalmente asumirá que la llamada fue exitoso.

Básicamente, a lo que me refiero es que el 99.99% del tiempo, el if (self! = Nil) no te compra nada en términos de mayor robustez, ya que solo le estás pasando la pelota a tu invocador. Para ser realmente capaz de manejar esto con solidez, en realidad necesitaría poner cheques en toda su jerarquía de llamadas. Y aun así, lo único que le compraría es que su aplicación fallará un poco más limpia y robusta. Pero todavía fallaría.

Si una clase de la biblioteca arbitrariamente decidió devolver el valor cero como resultado de una [superinicial], de todos modos se le puede fingir de todos modos, y eso es más una indicación de que el escritor de la clase de la biblioteca cometió un error de implementación.

Creo que esto es más una sugerencia de codificación heredada, cuando las aplicaciones se ejecutan en memoria mucho más limitada.

Pero para el código de nivel C, normalmente verificaría el valor de retorno de malloc () contra un puntero NULL. Mientras que para Objective-C, hasta que encuentre evidencia de lo contrario, creo que generalmente omito los controles if (self! = Nil). ¿Por qué la discrepancia?

Porque, en los niveles C y malloc, en algunos casos, en realidad puedes recuperarte parcialmente. Mientras que creo que en Objective-C, en el 99,99% de los casos, si [superinicial] devuelve nulo, básicamente se te dispara, incluso si tratas de manejarlo. También puedes dejar que la aplicación se bloquee y lidiar con las secuelas.


En OS X, no es tan probable que -[NSObject init] falle debido a razones de memoria. Lo mismo no se puede decir de iOS.

Además, es una buena práctica escribir cuando se subclasifica una clase que puede devolver nil por la razón que sea.


Este idioma particular es estándar porque funciona en todos los casos.

Si bien es poco común, habrá casos en que ...

[super init];

... devuelve una instancia diferente, lo que requiere la asignación a sí mismo.

Y habrá casos en los que devolverá nulo, por lo que requerirá la verificación nula para que su código no intente inicializar una ranura de variable de instancia que ya no exista.

La conclusión es que es el patrón correcto documentado para usar y, si no lo está utilizando, lo está haciendo mal.


Esto es para verificar que la intialazación funcionó, la instrucción if devuelve true si el método init no devolvió nada, por lo que es una manera de verificar que la creación del objeto funcionó correctamente. Hay pocas razones por las que pueda pensar que el init puede fallar, tal vez sea un método init invalidado que la súper clase no conoce o algo por el estilo, pero no creo que sea tan común. Pero si sucede, es mejor que no suceda nada que suponga un bloqueo así que siempre se verifica ...


Normalmente, si su clase se deriva directamente de NSObject , no será necesario. Sin embargo, es un buen hábito para entrar, ya que si su clase proviene de otras clases, sus inicializadores pueden devolver nil , y si es así, su inicializador puede capturar eso y comportarse correctamente.

Y sí, para que conste, sigo las mejores prácticas y las escribo en todas mis clases, incluso aquellas derivadas directamente de NSObject .


Por ejemplo:

[[NSData alloc] initWithContentsOfFile:@"this/path/doesn''t/exist/"]; [[NSImage alloc] initWithContentsOfFile:@"unsupportedFormat.sjt"]; [NSImage imageNamed:@"AnImageThatIsntInTheImageCache"];

... y así. (Nota: NSData podría arrojar una excepción si el archivo no existe). Hay bastantes áreas en las que el comportamiento esperado es el retorno de nil cuando se produce un problema, y ​​debido a esto, es una práctica estándar verificar nil casi todo el tiempo, por consistencia.


Tienes razón, a menudo podrías simplemente escribir [super init] , pero eso no funcionaría para una subclase de cualquier cosa. La gente prefiere simplemente memorizar una línea estándar de código y usarla todo el tiempo, incluso cuando a veces es necesaria, y así obtenemos el estándar if (self = [super init]) , que toma la posibilidad de que no se devuelva nada y la posibilidad de que un objeto que no sea el self sea ​​devuelto a la cuenta.


Un error común es escribir

self = [[super alloc] init];

que devuelve una instancia de la superclase, que NO es lo que quieres en una subclase constructor / init. Usted recupera un objeto que no responde a los métodos de la subclase, lo cual puede ser confuso y genera errores confusos acerca de no responder a métodos o identificadores no encontrados, etc.

self = [super init];

es necesario si la superclase tiene miembros (variables u otros objetos) para inicializar primero antes de configurar los miembros de las subclases. De lo contrario, el tiempo de ejecución objc los inicializa a 0 o a cero . (a diferencia de ANSI C, que a menudo asigna trozos de memoria sin eliminarlos del todo )

Y sí, la inicialización de la clase base puede fallar debido a errores de falta de memoria, componentes faltantes, fallas en la adquisición de recursos, etc. por lo que una comprobación de nil es acertada y toma menos de unos pocos milisegundos.


Esto es una especie de resumen de los comentarios anteriores.

Digamos que la superclase devuelve nil . ¿Qué va a pasar?

Si no sigues las convenciones

Tu código se bloqueará en medio de tu método init . (a menos que init no haga nada de importancia)

Si sigues las convenciones, no sabiendo que la superclase puede devolver nada (la mayoría de las personas terminan aquí)

Su código probablemente se bloqueará en algún momento más adelante, porque su instancia es nil , donde esperaba algo diferente. O su programa se comportará inesperadamente sin fallar. ¡Oh querido! ¿Quieres esto? No lo sé...

Si sigues las convenciones, de buena gana permites que tu subclase devuelva nada

La documentación de su código (!) Debe indicar claramente: "devuelve ... o nulo", y el resto de su código debe estar preparado para manejarlo. Ahora tiene sentido.