Tengo una pregunta general sobre cómo escribir métodos init en Objective-C.
Veo en todas partes (código de Apple, libros, código fuente abierto, etc.) que un método init debe 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 va a volver init alguna vez? Si llamé a init en NSObject y obtuve nada, 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 el método init de una clase pueda devolver nulo? Si es así, ¿en qué caso y por qué?
objective-c
null
init
Jasarien
fuente
fuente
Respuestas:
Por ejemplo:
... y así. (Nota: NSData podría generar una excepción si el archivo no existe). Hay bastantes áreas en las que el comportamiento esperado es devolver nulo cuando se produce un problema, y debido a esto es una práctica estándar verificar nulo casi todo el tiempo, por razones de coherencia.
fuente
Este modismo particular es estándar porque funciona en todos los casos.
Si bien es poco común, habrá casos en los que ...
... devuelve una instancia diferente, lo que requiere la asignación a sí mismo.
Y habrá casos en los que devolverá nil, 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á usando, lo está haciendo mal.
fuente
-init
adicto ...)[super init]
devuelva nulo cuando la superclase directa esNSObject
? ¿No es este un caso donde "todo está roto?"NSObject
es la superclase directa. Pero ... incluso si declarasNSObject
como la superclase directa, algo podría haberse modificado en tiempo de ejecución de tal manera queNSObject
la implementación deinit
no es lo que realmente se llama.Creo que, en la mayoría de las clases, si el valor de retorno de [super init] es nulo y lo verifica, como lo recomiendan las prácticas estándar, y luego regresa prematuramente si es nulo, básicamente su aplicación todavía no funcionará correctamente. Si se piensa en ello, a pesar de que si (auto! = Nil) cheque está ahí, para un correcto funcionamiento de la clase, el 99,99% de las veces que realmente haces necesidad de auto para ser no nula. Ahora, supongamos que, por cualquier razón, [súper init] hizo nula rentabilidad, básicamente, su cheque contra nula es, básicamente, pasando la pelota a la persona que llama de la clase, donde sería probable es que no todos modos, ya que, naturalmente, asumir que la llamada era exitoso.
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 realmente poder manejar esto de manera robusta, en realidad necesitaría poner controles en toda su jerarquía de llamadas. E incluso entonces, lo único que te compraría es que tu aplicación fallará de manera más limpia / robusta. Pero aún fallaría.
Si una clase de biblioteca decidió arbitrariamente devolver nil como resultado de un [superiniciado], de todos modos estás 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 ejecutaron en una memoria mucho más limitada.
Pero para el código de nivel C, todavía 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 omitiré las verificaciones if (self! = Nil). ¿Por qué la discrepancia?
Porque, en los niveles de C y malloc, en algunos casos puede recuperarse parcialmente. Mientras que pienso en Objective-C, en el 99.99% de los casos, si [super init] devuelve nulo, básicamente estás jodido, incluso si intentas manejarlo. También podría dejar que la aplicación se bloquee y lidiar con las consecuencias.
fuente
Este es un resumen de los comentarios anteriores.
Digamos que la superclase regresa
nil
. ¿Qué va a pasar?Si no sigues las convenciones
Tu código se bloqueará en medio de tu
init
método. (a menosinit
que no haga nada significativo)Si sigue las convenciones, sin saber que la superclase podría volver nula (la mayoría de las personas terminan aquí)
Es probable que su código se bloquee en algún momento más tarde, porque su instancia es
nil
, donde esperaba algo diferente. O tu programa se comportará inesperadamente sin fallar. ¡Oh querido! ¿Quieres esto? No lo sé...Si sigues las convenciones, permites voluntariamente que tu subclase devuelva cero
La documentación de su código (!) Debe indicar claramente: "devuelve ... o nulo", y el resto de su código debe estar preparado para manejar esto. Ahora tiene sentido.
fuente
Por lo general, si su clase se deriva directamente de
NSObject
, no necesitará hacerlo. Sin embargo, es un buen hábito entrar, ya que si su clase se deriva de otras clases, sus inicializadores pueden regresarnil
, 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 aquellas derivadas directamente
NSObject
.fuente
Foo *bar = [[Foo alloc] init]; if (bar) {[bar doStuff];}
NSObject
no garantiza-init
queNSObject
también te dé , si se cuentan tiempos de ejecución exóticos como versiones anteriores de GNUstep (que regresaGSObject
), así que no importa qué, un cheque y una asignación.Tienes razón, a menudo podrías escribir
[super init]
, pero eso no funcionaría para una subclase de cualquier cosa. Las personas prefieren simplemente memorizar una línea de código estándar y usarla todo el tiempo, incluso cuando solo es a veces necesario, y así obtenemos el estándarif (self = [super init])
, que tiene la posibilidad de que se devuelva nulo y la posibilidad de un objeto que noself
sea devuelto en cuenta.fuente
Un error común es escribir
que devuelve una instancia de la superclase, que NO es lo que desea en un constructor / init de subclase. Obtiene un objeto que no responde a los métodos de la subclase, que puede ser confuso, y genera errores confusos sobre no responder a métodos o identificadores no encontrados, etc.
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 fragmentos 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 de adquisición de recursos, etc., por lo que una comprobación de nulo es inteligente y toma menos de unos pocos milisegundos.
fuente
Esto es para verificar 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 verificar que la creación del objeto funcionó correctamente. Pocas razones por las que puedo pensar que ese init podría fallar, tal vez sea un método init anulado que la superclase desconoce o algo por el estilo, aunque no creo que sea tan común. Pero si sucede, es mejor que no suceda un accidente, supongo que siempre se verifica ...
fuente
En OS X, no es tan probable
-[NSObject init]
que falle debido a razones de memoria. No se puede decir lo mismo para iOS.Además, es una buena práctica escribir cuando se subclasifica una clase que podría regresar
nil
por cualquier razón.fuente
-[NSObject init]
es muy poco probable que falle debido a razones de memoria, ya que no asigna ninguna memoria.