En Objective-C ¿por qué debería comprobar si self = [super init] no es nil?


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 comprobar si self =[super init] no es nil 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 volverá a cero? Si llamé a init en NSObject y recuperé nil, entonces algo debe estar realmente jodido, ¿verdad? Y en ese caso, es mejor que ni siquiera escribas un programa...

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

Author: Jasarien, 2009-08-17

9 answers

Por ejemplo:

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

... y así sucesivamente. (Nota: NSData podría lanzar una excepción si el archivo no existe). Hay bastantes áreas donde devolver nil es el comportamiento esperado cuando se produce un problema, y debido a esto es una práctica estándar comprobar si hay nil prácticamente todo el tiempo, por el bien de la consistencia.

 51
Author: iKenndac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-17 13:30:18

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

Aunque poco común, habrá casos donde...

[super init];

... devuelve una instancia diferente, por lo que requiere la asignación a self.

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

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

 49
Author: bbum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-17 17:04:59

Creo que, en la mayoría de las clases, si el valor devuelto desde [super init] es nil y lo compruebas, como recomiendan las prácticas estándar, y luego devuelves prematuramente si es nil, básicamente tu aplicación todavía no va a funcionar correctamente. Si usted piensa en ello, a pesar de que si (auto != nil) check está ahí, para el correcto funcionamiento de su clase, el 99.99% de las veces que realmente hace necesita ser no-nil. Ahora, supongamos, por cualquier razón, [super init] hizo retorno cero, básicamente su check against nil es básicamente pasar el dinero a la persona que llama de su clase, donde probablemente fallaría de todos modos, ya que asumirá naturalmente que la llamada fue exitosa.

Básicamente, lo que quiero decir es que el 99.99% de las veces, el if (self != nil) no te compra nada en términos de mayor robustez, ya que solo estás pasando el dinero a tu invocador. Para ser realmente capaz de manejar esto de forma robusta, en realidad tendría que poner en cheques en toda su llamada jerarquía. E incluso entonces, lo único que te compraría es que tu aplicación fallaría un poco más limpio/robusto. Pero aún así fracasaría.

Si una clase de biblioteca decidió arbitrariamente devolver nil como resultado de un [super init], de todos modos, está bastante jodido, y eso es más una indicación de que el escritor de la clase de biblioteca cometió un error de implementación.

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

Pero para el código de nivel C, todavía normalmente verificaría el valor devuelto de malloc() contra un puntero NULO. Mientras que, para Objective-C, hasta que encuentre evidencia de lo contrario, creo que generalmente omitiré el if (self!= cero) comprobaciones. ¿Por qué la discrepancia ?

Porque, en los niveles de C y malloc, en algunos casos realmente se puede recuperar parcialmente. Mientras que creo que en Objective-C, en el 99.99% de los casos, si [super init] devuelve cero, estás básicamente jodido, incluso si intentas manejar se. También podría dejar que la aplicación se bloquee y lidiar con las consecuencias.

 25
Author: Gino,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-17 15:41:45

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, sin saber que la superclase podría devolver nil (la mayoría de la gente termina aquí)

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

Si sigues las convenciones, de buena gana permitiendo que tu subclase devuelva nil

Su documentación de código (!) debe indicar claramente: "devuelve ... o nil", y el resto de su código debe estar preparado para manejar esto. Ahora tiene sentido.

 8
Author: user123444555621,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-18 12:01:50

Normalmente, si tu clase se deriva directamente de NSObject, no necesitarás hacerlo. Sin embargo, es un buen hábito entrar, ya que si su clase deriva 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 en las que derivan directamente de NSObject.

 7
Author: John Rudy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-17 13:38:39

Tienes razón, a menudo podrías 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 solo es necesario a veces, y así obtenemos el estándar if (self = [super init]), que tiene en cuenta tanto la posibilidad de que se devuelva nil como la posibilidad de que se devuelva un objeto que no sea self.

 3
Author: andyvn22,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-17 16:52:35

Un error común es escribir

self = [[super alloc] init];

Que devuelve una instancia de la superclase, que NO es lo que quieres en un constructor de subclase/init. Recuperas un objeto que no responde a los métodos de la subclase, lo que puede ser confuso, y genera errores confusos sobre no responder a los 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 las subclases' miembro. De lo contrario, el tiempo de ejecución de objc los inicializa 0 o a nil. ( a diferencia de ANSI C, que a menudo asigna trozos de memoria sin borrarlos en absoluto )

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. así que un cheque por cero es sabio, y toma menos de unos pocos milisegundos.

 3
Author: Chris Reid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-24 06:40:06

Esto es para comprobar que la intialazation funcionó, la instrucción if devuelve true si el método init no devolvió nil, por lo que es una forma de comprobar que la creación del objeto funcionó correctamente. Pocas razones por las que puedo pensar que init podría fallar tal vez es un método init anulado que la super clase no conoce o algo por el estilo, aunque no creo que sea tan común. Pero si sucede, es mejor que nada suceda que un accidente supuse que siempre está comprobado...

 2
Author: Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-17 18:48:27

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

También, es una buena práctica para escribir cuando subclase una clase que podría devolver nil por cualquier razón.

 1
Author: MaddTheSane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-14 02:44:49