2009-08-27 7 views
29

Sono del mondo C++ in modo che il concetto di assegnazione this fa venire i brividi:Assegnazione di auto in Objective-C

this = new Object; // Gah! 

Ma in Objective-C v'è una parola chiave simile, self, per il quale questo è perfettamente accettabile:

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

Un sacco di codice di esempio Objective-C utilizza la linea di cui sopra in init routine. Le mie domande:

1) Perché assegnazione di self un senso (risposte del tipo "perché la lingua gli permette" non contano)

2) Cosa succede se non si assegna self nella mia routine di init ? Sto mettendo la mia istanza in una specie di pericolo?

3) Quando l'istruzione seguente if fallisce, cosa significa e cosa devo fare per recuperare da esso:

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

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

    return self; 
} 
+10

Perché le persone sono così sorprese quando lingue diverse hanno convenzioni e regole diverse? Se non lo facessero, sarebbero tutti uguali. –

+1

In questo caso particolare, Objective-C è strettamente correlato a C e C++ e la nozione di assegnare "questo" in quelle lingue ha una connotazione molto diversa dall'assegnazione di 'self' – fbrereto

+6

Objective-C è strettamente correlata a C, non a C++ . È totalmente estraneo al C++ e quindi ha una convenzione diversa per gestire gli oggetti e per inizializzare gli oggetti. –

risposta

33

Questo è un argomento che viene spesso sfidato da nuovi arrivati:

Fondamentalmente, nasce dall'idea che una superclasse può avere sovrascritta l'inizializzatore designato per restituire un oggetto diverso da quello restituito da +alloc. Se non è stato assegnato il valore di ritorno dell'inizializzatore super a self, è possibile che si tratti di un oggetto parzialmente inizializzato (poiché l'oggetto che è stato inizializzato con lo super non è lo stesso oggetto che si sta inizializzando).

Nel complesso, è piuttosto raro che super restituisca qualcosa di diverso, ma si verifica in un paio di casi.

+3

Buona risposta. Inoltre, quando viene per Objective-C, mi fido sempre di me ** @ bbum ** ... http://stackoverflow.com/questions/1287950/#1289199 –

+2

Hai fatto l'ipotesi errata che non cambi mai nulla sul web e che questi link La rete cambia, metà dei collegamenti sono interrotti. Per favore, rispondendo alle domande, RISPONDIAMO ALLA DOMANDA. Citare le cose e procurarle con i link va bene, ma in realtà scrivi una risposta! – ArtOfWarfare

+1

@ArtOfWarfare cheers –

1

Sono ancora nuovo nell'Obiettivo C, ma lo this post mi ha aiutato a capirlo.

Per riassumere, la maggior parte delle chiamate di init restituisce lo stesso oggetto a cui è già stato inizializzato. Se c'è un errore, init restituirà nil. Inoltre, alcuni oggetti come singoletti o oggetti unici (come NSNumber 0) restituiranno un oggetto diverso da quello inizializzato (il singleton o un oggetto globale 0). In queste situazioni è necessario avere un riferimento automatico a quell'oggetto. Non sono affatto un esperto in quello che succede dietro le quinte qui, ma ha un senso in superficie, per me.

10

In Objective-C, gli inizializzatori hanno la possibilità di restituire nil in caso di errore o restituire un oggetto completamente diverso da quello su cui è stato chiamato l'inizializzatore (NSArray esegue sempre questo, ad esempio). Se non si acquisisce il valore restituito di init, il metodo potrebbe essere in esecuzione nel contesto di un oggetto deallocato.

Some people in disaccordo sul fatto che si dovrebbe fare tutta la manfrina assegnare a sé se non si aspettano per ottenere qualcos'altro indietro dal inizializzatore superclasse, ma è generalmente considerato come una buona codifica difensivo.

E sì, sembra strano.

3

Tutti gli altri punti qui sono validi, ma è importante capire anche che self è un parametro implicito per ogni metodo Objective-C (objc_msgSend() lo passa) e può essere scritto su, proprio come qualsiasi altro parametro di metodo . (Scrivere su parametri espliciti è generalmente disapprovato, a meno che non siano fuori parametri.)

In genere, questo viene fatto solo nel metodo -init, per i motivi che altri hanno affermato. Ha effetto solo perché self viene restituito dal metodo e utilizzato nell'assegnazione id obj = [[NSObject alloc] init]; Influisce anche sulla risoluzione implicita di ivars, perché, ad esempio, se myVar è un ivar della mia classe, quindi accedervi in ​​un metodo lo fa essere implicitamente risolto a self->myVar.

1

Se [super init] restituisce nil vuol dire che sei stato deallocato e il tuo parametro self ora è un puntatore non valido. Seguendo ciecamente la convenzione self = [super init], si salverà da potenziali bug cattivi.

Si consideri il seguente inizializzatore non tipico:

- (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. 
     ... 
    } 
} 

Ora che cosa succede se il mio superclasse decide di abortire e nil tornare? Sono stato disallocato e il mio parametro self non è più valido e [self release] si bloccherà. Riassegnando self, evito l'incidente.

6

È vero che init può restituire nil, se l'inizializzazione non riesce. Ma questo non è il motivo principale per cui dovresti assegnarti a te stesso quando implementi i tuoi inizializzatori.

È stato menzionato prima, ma è necessario sottolineare ancora più difficile: l'istanza restituita da un inizializzatore potrebbe non essere la stessa istanza di quella inviata, infatti potrebbe non essere nemmeno della stessa classe!

Alcune classi lo utilizzano come standard, ad esempio tutto l'inizializzatore su NSString e NSArray restituirà sempre una nuova istanza di una classe diversa. Gli inizializzatori su UIColor restituiranno spesso un'istanza diversa di una classe specializzata.

E tu stesso puoi happely implementare qualcosa di simile a questo se si vuole:

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

questo ti permette di uscire l'attuazione di alcune caso particolare in una classe separata, senza richiedere i clienti del vostro API per la cura o persino saperlo.