2009-08-27 6 views
29

Soy del mundo C++ por lo que la idea de asignar this Me hace temblar:La asignación a uno mismo en Objective-C

this = new Object; // Gah! 

Pero en Objective-C no es una palabra clave similares, self, para el que este es perfectamente aceptable:

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

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

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

2) ¿Qué pasa si no se asigna self en mi rutina init ? ¿Estoy poniendo mi caso en algún tipo de peligro?

3) Cuando la sentencia que sigue if falla, ¿qué significa y qué debo hacer para recuperarse de ella:

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

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

    return self; 
} 
+10

¿Por qué la gente está tan sorprendida cuando diferentes idiomas tienen diferentes convenciones y reglas diferentes? Si no lo hicieran, todos serían iguales. –

+1

En este caso particular, Objective-C está estrechamente relacionado con C y C++, y la noción de asignar 'this' en esos idiomas tiene una connotación muy diferente a la de 'self' – fbrereto

+6

Objective-C está estrechamente relacionado con C, no con C++ . No tiene ninguna relación con C++ y, por lo tanto, tiene una convención diferente para tratar con objetos y para la inicialización de objetos. –

Respuesta

33

Este es un tema que con frecuencia se ve desafiada por los recién llegados:

Básicamente, se deriva de la idea de que una superclase puede tener sobre-montado el inicializador designado para devolver un objeto diferente a la que regresó de +alloc. Si no asignó el valor de retorno del inicializador super al self, entonces podría tratarse de un objeto parcialmente inicializado (porque el objeto que super inicializó no es el mismo objeto que está inicializando).

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

+3

Buena respuesta. Además, cuando se trata para Objective-C, siempre confío en ** @ bbum ** ... http://stackoverflow.com/questions/1287950/#1289199 –

+2

Has asumido erróneamente que nada en la web cambia alguna vez y que estos enlaces para siempre ser útil. La web cambia, la mitad de sus enlaces están rotos. Por favor, cuando responda preguntas, RESPONDA LA PREGUNTA. Citar cosas y buscarlas con enlaces está bien, ¡pero en realidad escribe una respuesta! – ArtOfWarfare

+1

@ArtOfWarfare vitorea –

1

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

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

10

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

Some people no están de acuerdo acerca de si debe hacer todo el galimatías-asignar a uno mismo si no esperar a conseguir algo más atrás de la inicializador superclase, pero es generalmente considerado como una buena codificación defensiva.

Y sí, se ve raro.

3

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

Por lo general, esto solo se hace en el método -init, por las razones que otros han establecido. Solo tiene algún efecto porque self se devuelve del 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, el acceso a él en un método hace que se resuelve implícitamente a self->myVar.

1

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

Considere lo siguiente inicializador no típica:

- (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 regresar a cero? He sido desasignado y mi parámetro self ahora no es válido y [self release] se bloqueará. Al volver a asignar self, evito ese bloqueo.

6

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

Se ha mencionado anteriormente, pero es necesario insistir aún más: la instancia devuelta desde 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 un estándar, por ejemplo, todos los inicializadores a NSString y NSArray siempre devolverán una nueva instancia de una clase diferente. Los inicializadores al UIColor devolverán frecuentemente una instancia diferente de una clase especializada.

Y usted puede implementar happely algo como esto si quieres:

-(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 romper cabo la implementación de algún caso especial en una clase separada, sin necesidad de los clientes de su API para cuidar o incluso saber sobre eso.

Cuestiones relacionadas