Diciamo che creo la mia class e il suo metodo init
. Perché dovrei chiamare e restituire il valore della superclass init
assegnata a self? In quali casi copre?
Gradirei gli esempi perché ne avrei bisogno per la superclass e il non-cocoa Cocoa.
Intendi perché?
self = [super init];
piuttosto che
[super init];
Due ragioni:
Modificato in risposta al commento di Michael:
Posso capire perché ho bisogno di salvare e restituire [super init]. Ma è solo convenzione e bell’aspetto che ci fa usare come variabile temporanea per passare il risultato?
N. Le variabili di istanza sono accessibili in relazione al puntatore automatico, quindi nel modo seguente:
-(id) init { self = [super init]; if (self != nil) { myBoolIvar = YES; // The above is an implicit version of self->myBoolIvar = YES; } return self; }
il sé deve chiaramente indicare il giusto blocco di memoria, cioè quello che si intende restituire.
L’altro punto è che se super init restituisce un’istanza di class diversa, il resto del codice dopo quella linea potrebbe non avere senso, portare a perdite di memoria e arresti anomali, senza nemmeno parlare dell’istanza creata da quella class.
Questo potrebbe essere un problema. Se ho sottoclassato NSNumber e [super init] ho deciso di restituire un NSString (che potrebbe – non c’è nulla che lo impedisca) che sarebbe chiaramente un disastro. Qualunque super-ritorni da -init deve essere “compatibile” con la sottoclass nel senso di fornire spazio per ivars ed essere ulteriormente sottoclassato o è un bug orrendo (a meno che, ovviamente, il problema non sia documentato). Quindi, in generale, non devi preoccuparti di controllare la class. Tuttavia, leggere la documentazione. Si veda ad esempio la sezione sulla sottoclassi di NSString nei documenti di NSString.
So che è un po ‘tardi per la mia risposta, ma non posso impedirmi di pubblicare un link che ho trovato molto utile per chiarire il mio dubbio su questo problema.
Matt Gallagher: Cosa significa quando assegni [super init] a sé?
EDIT: Come da commenti, ecco i punti essenziali del link
Per capire perché self=[super init];
dobbiamo considerare molti punti. Affrontiamolo uno per uno.
Cos’è il self
Ogni metodo ha due parametri nascosti: self
e _cmd
. Quindi la chiamata al metodo
- (id)initWithString:(NSString *)aString
viene modificato dal compilatore in una chiamata di funzione come questa
id initWithString(id self, SEL _cmd, NSString *aString);
Perché abbiamo bisogno di noi stessi?
La realtà è che il compilatore utilizza il parametro self
per risolvere qualsiasi riferimento a una variabile di istanza all’interno di un metodo.
Supponiamo di avere un metodo setValueToZero
e value
è una variabile di istanza della class a cui appartiene, quindi l’implementazione
- (void)setValueToZero { value = 0; }
sarà convertito dal compilatore in una funzione come questa
void setValueToZero(id self, SEL _cmd) { self->value = 0; }
Il self
già un valore quando viene chiamato init
?
Di seguito è riportato un esempio di creazione e inizializzazione di un object tipico.
[[MyClass alloc] initWithString:@"someString"]
Qui, nel momento in cui initWithString
metodo initWithString
, self avrà come object l’object appena assegnato (ovvero il valore restituito da [MyClass alloc]
). In effetti, è quasi garantito il valore finale corretto.
Perché self = [super init];
?
È perché [super init]
è autorizzato a fare una delle tre cose:
self
non cambia) con i valori di istanza ereditati inizializzati. nil
, indicando un errore. Nel primo caso, l’assegnazione non ha alcun effetto su di self
. Nel terzo caso, l’inizializzazione è fallita, self
è impostato su nil
e viene restituito.
La ragione dietro l’assegnazione a self
è con il secondo caso. Considera quanto segue
- (id)initWithString:(NSString *)aString { self = [super init]; if (self) { instanceString = [aString retain]; } return self; }
Vogliamo la conversione da
instanceString = [aString retain];
a
self->instanceString = [aString retain];
agire sul valore corretto e quindi dobbiamo cambiare il valore di self
.
Quando [super init]
restituisce un object diverso?
In una delle seguenti situazioni
[NSNumber numberWithInteger:0]
restituisce sempre l’object “zero” globale) In tutti tranne il caso finale, continuare a inizializzare l’object restituito se cambia è un errore – l’object restituito è già completamente inizializzato e non è più necessario correlato alla class. Quindi un approccio di init migliore sarà il seguente
- (id)initWithString:(NSString *)aString { id result = [super init]; if (self == result) { instanceString = [aString retain]; } return result; }
Conclusione
Non è necessario assegnare [super init]
a self
per far funzionare la maggior parte delle classi. In alcuni casi oscuri, è in realtà la cosa sbagliata da fare.
Allora, perché continuiamo ad assegnarci a self
? È il modello tradizionale per un inizializzatore e, sebbene in alcuni casi sia sbagliato, è giusto in altri casi che sono stati scritti per prevedere questo approccio.
Fondamentalmente ogni class Objective-C è una sottoclass. È o una class che hai specificato o NSObject.
Nella sottoclass (la tua class su cui stai lavorando) chiami self = [super init]
Ciò che sostanzialmente fa è chiamare il metodo init della super class (quelli che ho menzionato sopra) (il costruttore) e lo assegna alla class corrente.
Questo assicura che venga chiamato il metodo di inizializzazione delle superclassi.
Now for If (self) In pratica controlla se il codice di cui sopra funziona.
Questo è fatto per assicurare che se chiami qualche variabile istanza della super class, sarai in grado di farlo.
Nella maggior parte dei casi, l’impostazione di sé per [super init] non fa nulla poiché [super init] si riavvolgerà comunque. Ci sono alcuni casi rari, tuttavia, in cui [super init] restituirà qualcosa di diverso. Può tornare a zero se non riesce a inizializzare la superclass per qualche motivo o può decidere di restituire un object completamente diverso.
Implementazione di Initializer designato
**
** All’interno di un metodo, self è una variabile locale implicita. Non è necessario dichiararlo e viene automaticamente impostato per puntare all’object a cui è stato inviato il messaggio. (Qualcosa di simile nella programmazione Android.) In genere, viene utilizzato self che un object può inviare un messaggio a se stesso. Esempio qui:
return self;
**
** Quando sostituiamo un metodo, vogliamo mantenere il metodo della superclass e fare in modo che la sottoclass aggiunga qualcosa di nuovo. Per semplificare, c’è una direttiva del compilatore in Objective-C chiamata super. Come funziona super? Di solito quando si invia un messaggio a un object, la ricerca di un metodo con quel nome inizia nella class dell’object. Se non esiste un tale metodo, la ricerca continua nella superclass dell’object. La ricerca continuerà la gerarchia dell’ereditarietà fino a trovare un metodo adatto. (Se raggiunge la cima della gerarchia e non viene trovato alcun metodo, viene generata un’eccezione). Quando inviamo un messaggio a super, stiamo inviando un messaggio a self, ma la ricerca del metodo salta la class dell’object e inizia dalla superclass. Nel caso precedente, inviamo il messaggio init a super. Questo chiama il metodo init di NSObject.
Poiché devi in qualche modo inizializzare l’object, e presumibilmente vuoi fare un’inizializzazione, è necessaria una superclass, dal momento che stai scendendo da essa.