Come funziona un carattere di sottolineatura davanti a una variabile in una classe cacao obiettivo-c?


157

Ho visto in alcuni esempi di iPhone che gli attributi hanno usato un carattere di sottolineatura _ davanti alla variabile. Qualcuno sa cosa significa? O come funziona?

Un file di interfaccia che sto usando è simile a:

@interface MissionCell : UITableViewCell {
    Mission *_mission;
    UILabel *_missionName;
}

@property (nonatomic, retain) UILabel *missionName;

- (Mission *)mission;

Non sono sicuro di cosa faccia esattamente quanto sopra, ma quando provo a impostare il nome della missione come:

aMission.missionName = missionName;

Ottengo l'errore:

richiesta di membro "missionName" in qualcosa che non sia una struttura o unione

Risposte:


97

Se usi il prefisso di sottolineatura per i tuoi ivar (che non è altro che una convenzione comune, ma utile), allora devi fare 1 cosa in più in modo che l'accessorio generato automaticamente (per la proprietà) sappia quale ivar usare. Nello specifico, nel tuo file di implementazione, synthesizedovresti apparire così:

@synthesize missionName = _missionName;

Più genericamente, questo è:

@synthesize propertyName = _ivarName;

78
con proprietà di auto-sintesi non è più necessario. Xcode sintetizza un @property xxxx con un ivar chiamato _xxxx dietro le quinte. Neat.
LearnCocos2D,

@ LearnCocos2D Ciao! Un principiante per iOS qui e c'è qualcosa che devo chiarire. Per tutto questo tempo ciò che ho fatto è stato dichiarare la propertynel file h e nella .m fie ho accesso utilizzando selfin questo modo, self.someProperty. È questa la via giusta? O dovrei usare gli ivar nel codice?
Isuru,

l'impostazione dell'ivar non esegue il setter proprietà - decidi se è una buona idea o meno per ogni caso particolare
LearnCocos2D

Domanda Noob: perché non usare direttamente gli ivar? perché dovrei dichiarare un var separato per contenere l'ivar?
Allen

1
@Allen, se capisco correttamente la tua domanda: la var separata che stai dichiarando è un puntatore alla variabile effettiva. Questo è importante per alcuni motivi (di cui sono a conoscenza) Innanzitutto, quando si passa un puntatore a una funzione non si duplica il suo valore. Stai semplicemente dicendo alla funzione dove trovare il valore da usare. Questo aiuta a mantenere bassa la memoria usata (e aiuta anche con l'allocazione e il dealloc della memoria, che è importante in assenza di "garbage collection" che troverai in Java)
David Sigley,

18

È solo una convenzione per la leggibilità, non fa nulla di speciale per il compilatore. Vedrai le persone usarlo su variabili di istanza private e nomi di metodi. Apple in realtà consiglia di non utilizzare il carattere di sottolineatura (se non stai attento potresti scavalcare qualcosa nella tua superclasse), ma non dovresti sentirti male a ignorare quel consiglio. :)


19
Da quanto ho capito, Apple sconsiglia di utilizzare il prefisso di sottolineatura sui nomi dei metodi (si riservano questo come convenzione per i metodi privati), ma non hanno alcuna raccomandazione in merito ai nomi delle variabili di istanza.
Kelan,

9
@Kelan In effetti, Apple incoraggia a farlo : "Di solito, non dovresti accedere direttamente alle variabili di istanza, invece dovresti usare i metodi di accesso (devi accedere direttamente alle variabili di istanza nei metodi init e dealloc). Per aiutare a segnalare questo, prefisso istanza nomi di variabili con un trattino basso (_), ad esempio: \ @implementation MyClass {BOOL _showsTitle;} "
dmirkitanov

In realtà non penso che Apple ci incoraggi a farlo, dal momento che tutti i loro codici di esempio nella libreria per sviluppatori iOS non contengono ( ). Apple afferma inoltre di averlo riservato, il che significa che lo usano internamente per i propri framework come UIKit ecc. Ecco perché non dovremmo usarlo con noncuranza. Ma vedo che, nel link che hai fornito @kelan. In realtà dicono nella "cronologia delle revisioni" che è "adatto" da usare ( ). Interpreto come "possiamo" usarlo se vogliamo.
WYS,

La documentazione di Apple che dice di non usare il prefisso di sottolineatura per i nomi dei metodi è qui .
ThomasW,

9

L'unico scopo utile che ho visto è di distinguere tra variabili locali e variabili membro come indicato sopra, ma non è una convenzione necessaria. Se associato a un @property, aumenta la verbosità delle dichiarazioni di sintesi - @synthesize missionName = _missionName;ed è brutto ovunque.

Invece di usare il carattere di sottolineatura, basta usare nomi di variabili descrittivi all'interno di metodi che non sono in conflitto. Quando devono essere in conflitto, il nome della variabile all'interno del metodo deve presentare un carattere di sottolineatura, non la variabile membro che può essere utilizzata da più metodi . L'unico posto comune utile è in un setter o in un metodo init. Inoltre, renderà l'istruzione @synthesize più concisa.

-(void)setMyString:(NSString*)_myString
{
    myString = _myString;
}

Modifica: con l'ultima funzionalità del compilatore di auto-sintesi, ora uso il trattino basso per l'ivar (nella rara occasione in cui ho bisogno di usare un ivar per abbinare ciò che fa l'auto-sintesi.


Al contrario. la variabile privata è sottolineata. la proprietà no. e chi li sintetizza li accoppi.
Justin,

È esattamente come descrivo, tranne per il fatto che l'ho definita una "variabile membro" anziché una "variabile privata".
Peter DeWeese,

Ahia! Questo richiede problemi ... l'auto-sintesi renderà ivar _myString il che significa che il setter non funzionerà (perché non sarà quindi in grado di distinguere il tuo ivar dal parametro del metodo).
geowar,

Corretto, motivo per cui ho aggiunto la modifica alla fine quando Apple ha aggiunto l'auto-sintesi.
Peter DeWeese,

5

In realtà non significa nulla, è solo una convenzione che alcune persone usano per differenziare le variabili membro dalle variabili locali.

Per quanto riguarda l'errore, sembra che aMission abbia il tipo sbagliato. Qual è la sua dichiarazione?


È comune negli IDE con intellisense; farà apparire le variabili del tuo membro / modulo / classe in cima all'elenco. Un altro prefisso comune è "m_"
STW

1
se non significa nulla, come puoi passare avanti e indietro tra _missionName e missionName come nel mio esempio sopra? La mia dichiarazione è simile a: Mission * aMission = [[Mission alloc] init]; aMission.missionName = @ "una missione";
Atma,

1
Uno è una variabile di istanza e l'altro è una proprietà. Non è possibile accedere alle variabili di istanza con sintassi come aMission.missionName, poiché tale sintassi non funziona con i puntatori.
Chuck,

Inoltre, tieni presente che stai cercando di operare su un oggetto Mission, ma l'interfaccia che hai pubblicato con la proprietà missionName è MissionCell.
smorgan,

2

Questo è solo per la convenzione di denominazione delle proprietà di sintesi.

Quando sintetizzi le variabili nel file .m, Xcode ti fornirà automaticamente un'intelligenza variabile.


1

La presenza di un carattere di sottolineatura non solo consente di risolvere i tuoi ivar senza ricorrere all'uso della sintassi self.member, ma rende il tuo codice più leggibile poiché sai quando una variabile è un ivar (a causa del suo prefisso di sottolineatura) o un argomento membro (nessun carattere di sottolineatura ).

Esempio:

- (void) displayImage: (UIImage *) image {

    if (image != nil) {
        // Display the passed image...
        [_imageView setImage: image];
    } else {
        // fall back on the default image...
        [_imageView setImage: _image];
    }
}

In questo esempio sarebbe bello vedere anche un confronto sull'uso di self.image (o [immagine di sé]). Quando è meglio usare self.image e quando è meglio usare _image?
Boeckm,

2
@Boeckm: in genere, è necessario utilizzare per self.imageaccedere alla proprietà. L'unica volta in cui dovresti accedere alla variabile di istanza, _imageè direttamente all'interno dei initmetodi e il deallocmetodo, quando si chiama qualsiasi altro metodo può essere rischioso (poiché l'oggetto è metà inizializzato o metà deallocato).
Peter Hosey,

1

Questo sembra essere l'elemento "principale" per domande su self.variableName vs. _variablename. Ciò che mi ha gettato per un loop è stato che nel .h, avevo:

...
@interface myClass : parentClass {
className *variableName;    // Note lack of _
}

@property (strong, nonatomic) className  *variableName;
...

Questo porta a self.variableName e _variableName come due variabili distinte nel .m. Quello di cui avevo bisogno era:

...
@interface myClass : parentClass {
className *_variableName;    // Note presence of _
}

@property (strong, nonatomic) className  *variableName;
...

Quindi, nella classe '.m, self.variableName e _variableName sono equivalenti.

Ciò su cui non sono ancora chiaro è perché molti esempi funzionano ancora, anche se non è difficile.

raggio


0

invece di trattino basso puoi usare il nome self.variable oppure puoi sintetizzare la variabile per usare la variabile o l'outlet senza trattino basso.


2
se hai solo bisogno della variabile nella stessa classe, basta dichiararla nel file .m stesso, ti permetterà di chiamare senza sé né il carattere di sottolineatura
Ansal Antony,

0

Manca dalle altre risposte che l'uso _variableti impedisce di digitare variablee accedere distrattamente a ivar anziché alla proprietà (presumibilmente intesa).

Il compilatore ti costringerà a utilizzare self.variableo _variable. L'uso di caratteri di sottolineatura rende impossibile digitare variable, riducendo gli errori del programmatore.

- (void)fooMethod {

    // ERROR - "Use of undeclared identifier 'foo', did you mean '_foo'?"
    foo = @1;

    // So instead you must specifically choose to use the property or the ivar:

    // Property
    self.foo = @1;

    // Ivar
    _foo = @1;

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.