Asignar a uno mismo en Objective-C


Soy del mundo de C++, así que la noción de asignar this me hace estremecer:

this = new Object; // Gah!

Pero en Objective-C hay una palabra clave similar, self, para la cual esto es perfectamente aceptable:

self = [super init]; // wait, what?

Una gran cantidad de código de ejemplo de Objective-C usa la línea anterior en las rutinas init. Mis preguntas:

1) ¿Por qué la asignación a self tiene sentido (respuestas como "porque el lenguaje lo permite" no cuentan)

2) ¿Qué sucede si no asigno self en mi rutina init? Soy yo poner mi ejemplo en algún tipo de peligro?

3) Cuando la siguiente declaración if falla, qué significa y qué debo hacer para recuperarme de ella:

- (id) init
{
    self = [super init];

    if (self)
    {
        self.my_foo = 42;
    }

    return self;
}
Author: fbrereto, 2009-08-27

6 answers

Este es un tema que es frecuentemente cuestionado por los recién llegados:

Básicamente, se deriva de la idea de que una superclase puede haber superado el inicializador designado para devolver un objeto que el devuelto desde +alloc. Si no asignaste el valor devuelto del inicializador de super a self, entonces podrías estar tratando con un objeto parcialmente inicializado (porque el objeto que super inicializó no es el mismo objeto que estás inicializando).

En general, es bastante raro que super devuelva algo diferente, pero sucede en un par de casos.

 33
Author: Dave DeLong,
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-12-30 05:55:54

En Objective-C, los inicializadores tienen la opción de devolver nil en caso de fallo o devolver un objeto completamente diferente al que se invocó al inicializador (NSArray siempre hace esto, por ejemplo). Si no captura el valor devuelto de init, el método podría estar ejecutándose en el contexto de un objeto desasignado.

Algunas personas no están de acuerdo sobre si debe hacer todo el rigamarole de asignarse a sí mismo si no espera obtener algo más de la inicializador de superclase, pero generalmente se considera que es una buena codificación defensiva.

Y sí, se ve raro.

 10
Author: Chuck,
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-27 15:26:18

Es cierto que init puede devolver nil, si la inicialización falla. Pero esta no es la razón principal por la que debe asignar a self cuando implementa sus propios inicializadores.

Se ha mencionado antes, pero es necesario enfatizar aún más: la instancia devuelta de un inicializador puede no ser la misma instancia que la que envió, de hecho, ¡puede que ni siquiera sea de la misma clase!

Algunas clases usan esto como estándar, por ejemplo all initializer a NSString y NSArray siempre devolverá una nueva instancia de una clase diferente. Los inicializadores a UIColor con frecuencia devolverán una instancia diferente de una clase especializada.

Y usted mismo puede implementar felizmente algo como esto si lo desea:

-(id)initWithName:(NSString*)name;
{
  if ([name isEqualToString:@"Elvis"]) {
    [self release];
    self = [[TheKing alloc] init];
  } else if (self = [super init]){
    self.name = name;
  }
  return self;
}

Esto le permite dividir la implementación de algún caso especial en una clase separada, sin requerir que los clientes de su API se preocupen o incluso lo sepan.

 6
Author: PeyloW,
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-28 21:56:30

Todos los demás puntos aquí son válidos, pero es importante que entienda también que self es un parámetro implícito para cada método Objective-C (objc_msgSend() lo pasa) y se puede escribir en él, al igual que cualquier otro parámetro de método. (Escribir en parámetros explícitos es generalmente mal visto, a menos que sean parámetros fuera.)

Normalmente, esto solo se hace en el método -init, por las razones que otros han indicado. Solo tiene algún efecto porque self se devuelve desde el método y se usa en la asignación id obj = [[NSObject alloc] init]; También afecta la resolución implícita de ivars, porque, por ejemplo, si myVar es un ivar de mi clase, entonces acceder a él en un método hace que se resuelva implícitamente a self->myVar.

 3
Author: kperryua,
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-27 17:37:59

Todavía soy nuevo en Objective C, pero este post me ayudó a entender esto.

Para resumirlo, la mayoría de las llamadas init devuelven el mismo objeto al que self ya está inicializado. Si hay un error, entonces init devolverá nil. Además, algunos objetos como singleton u objetos únicos (como NSNumber 0) devolverán un objeto diferente al inicializado (el singleton o un objeto global 0). En estas situaciones es necesario tener auto referencia a ese objeto. No estoy de ninguna manera un experto en lo que está pasando detrás de las escenas aquí, pero tiene sentido en la superficie, para mí.

 1
Author: Barry,
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-27 15:24:47

Si [super init] devuelve nil eso significa que ha sido desasignado y su parámetro self ahora es un puntero no válido. Siguiendo ciegamente la convención self = [super init] te salvarás de errores potencialmente desagradables.

Considere el siguiente inicializador no típico:

- (id)initWithParam:(id)param {
    if (!param) {
        // Bad param.  Abort
        self = [super init]; // What if [super init] returns nil?
        [self release];
        return nil;
    }
    else 
    {
        // initialize with param.
        ...
    }
}

Ahora, ¿qué sucede si mi superclase decide abortar y devolver nil? He sido desasignado y mi parámetro self ahora no es válido y [self release] se bloqueará. Al reasignar self, evito ese bloqueo.

 1
Author: Darren,
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-28 00:02:09