Jak działa podkreślenie przed zmienną w kakaowej klasie C-obiektywu?

157

W kilku przykładach iPhone'a widziałem, że atrybuty używają podkreślenia _ przed zmienną. Czy ktokolwiek wie, co to znaczy? Albo jak to działa?

Plik interfejsu, którego używam, wygląda następująco:

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

@property (nonatomic, retain) UILabel *missionName;

- (Mission *)mission;

Nie jestem pewien, co dokładnie robi powyższe, ale kiedy próbuję ustawić nazwę misji, taką jak:

aMission.missionName = missionName;

Wyskakuje mi błąd:

prośba o członkostwo „MissionName” w czymś, co nie jest strukturą ani związkiem

Atma
źródło

Odpowiedzi:

97

Jeśli używasz przedrostka podkreślenia dla swoich ivars (co jest niczym innym jak zwykłą konwencją, ale użyteczną), musisz zrobić 1 dodatkową rzecz, aby automatycznie wygenerowany akcesor (dla właściwości) wiedział, którego modułu ivar użyć. W szczególności w pliku implementacji synthesizepowinno wyglądać to następująco:

@synthesize missionName = _missionName;

Bardziej ogólnie jest to:

@synthesize propertyName = _ivarName;
Kelan
źródło
78
dzięki właściwościom autosyntetyzującym nie jest to już konieczne. Xcode syntetyzuje @property xxxx z ivar o nazwie _xxxx za kulisami. Schludny.
LearnCocos2D
@ LearnCocos2D Cześć! Jestem nowicjuszem w iOS i muszę coś wyjaśnić. Przez cały ten czas, co zrobiłem było zadeklarować propertyw pliku .h oraz w .m FIE mam dostępu do niego przy użyciu selfjak tak self.someProperty. Czy to dobra droga? A może powinienem używać ivars w kodzie?
Isuru,
ustawienie ivar nie uruchamia setera właściwości - sam decydujesz, czy to dobry pomysł, czy nie w każdym konkretnym przypadku
LearnCocos2D
Noob pytanie: dlaczego nie użyć ivars bezpośrednio? dlaczego miałbym zadeklarować oddzielną zmienną do przechowywania ivar?
Allen
1
@Allen, jeśli dobrze rozumiem twoje pytanie: Oddzielna zmienna, którą deklarujesz, jest wskaźnikiem do rzeczywistej zmiennej. Jest to ważne z kilku powodów (o których wiem) Po pierwsze, kiedy przekazujesz wskaźnik do funkcji, nie kopiujesz jej wartości. Po prostu mówisz funkcji, gdzie ma znaleźć wartość do użycia. Pomaga to utrzymać mało używanej pamięci (a także pomaga w przydzielaniu i zwalnianiu pamięci, co jest ważne w przypadku braku `` zbierania śmieci '', które można znaleźć w Javie)
David Sigley
18

To tylko konwencja czytelności, nie robi nic specjalnego dla kompilatora. Zobaczysz, jak ludzie używają go na prywatnych zmiennych instancji i nazwach metod. Apple faktycznie nie zaleca używania podkreślenia (jeśli nie jesteś ostrożny, możesz zastąpić coś w swojej superklasie), ale nie powinieneś czuć się źle, ignorując tę ​​radę. :)

Marc Charbonneau
źródło
19
Z tego, co rozumiem, Apple odradza używanie przedrostka podkreślenia w nazwach metod (zastrzegają to dla siebie jako konwencję dla metod prywatnych), ale nie mają takich zaleceń dotyczących nazw zmiennych instancji.
Kelan
9
@Kelan W rzeczywistości firma Apple zachęca do tego : „Zwykle nie należy uzyskiwać bezpośredniego dostępu do zmiennych instancji, zamiast tego należy używać metod akcesorów (dostęp do zmiennych instancji uzyskuje się bezpośrednio w metodach init i dealloc). Aby to zasygnalizować, przedrostek instancji nazwy zmiennych z podkreśleniem (_), na przykład: \ @implementation MyClass {BOOL _showsTitle;} "
dmirkitanov
Właściwie nie sądzę, aby Apple zachęcał nas do tego, ponieważ wszystkie ich własne przykładowe kody w bibliotece programisty iOS nie mają w sobie znaku ( ). Apple twierdzi również, że zarezerwowali to, co musi oznaczać, że używają go wewnętrznie do swoich własnych frameworków, takich jak UIKit itp. Dlatego nie powinniśmy go beztrosko używać. Ale widzę to w linku, który podałeś @kelan. W rzeczywistości mówią w „historii wersji”, że „nadaje się” do użycia ( ). Interpretuję tak, jak „możemy” go używać, jeśli chcemy.
WYS
Dokumentacja Apple, która mówi, aby nie używać przedrostka podkreślenia dla nazw metod, jest tutaj .
ThomasW,
9

Jedynym użytecznym celem, jaki widziałem, jest rozróżnienie między zmiennymi lokalnymi i zmiennymi składowymi, jak wspomniano powyżej, ale nie jest to niezbędna konwencja. W połączeniu z @właściwością zwiększa szczegółowość instrukcji syntetyzujących - @synthesize missionName = _missionName;i wszędzie jest brzydki.

Zamiast używać podkreślenia, po prostu używaj opisowych nazw zmiennych w metodach, które nie powodują konfliktów. W przypadku konfliktu nazwa zmiennej w metodzie powinna zawierać podkreślenie, a nie zmienną składową, która może być używana przez wiele metod . Jedynym typowym miejscem, w którym jest to przydatne, jest setter lub metoda inicjująca. Ponadto sprawi, że instrukcja @synthesize będzie bardziej zwięzła.

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

Edycja: Dzięki najnowszej funkcji kompilatora automatycznej syntezy używam teraz podkreślenia dla ivar (w rzadkich przypadkach, gdy potrzebuję użyć ivar, aby dopasować to, co robi automatyczna synteza.

Peter DeWeese
źródło
To na odwrót. zmienna prywatna jest podkreślona. nieruchomość nie. a kiedy je syntetyzujesz, łączysz je.
Justin
To jest dokładnie to, co opisuję, z tym wyjątkiem, że nazwałem to „zmienną składową” zamiast „zmienną prywatną”.
Peter DeWeese
Auć! To jest pytanie o kłopoty… auto-synteza spowoduje, że ivar _myString oznacza, że ​​setter nie będzie działał (ponieważ wtedy nie będzie w stanie odróżnić twojego ivar od parametru metody).
geowar
Prawidłowo, dlatego dodałem edycję na końcu, gdy Apple dodał autosyntezę.
Peter DeWeese
5

To tak naprawdę nic nie znaczy, to po prostu konwencja, której niektórzy ludzie używają do rozróżniania zmiennych składowych od zmiennych lokalnych.

Jeśli chodzi o błąd, to wygląda na to, że misja ma zły typ. Jaka jest jego deklaracja?

smorgan
źródło
Jest to powszechne w IDE z inteligencją; spowoduje to, że zmienne składowe / moduły / klasy będą wyświetlane na górze listy. Innym częstym prefiksem jest „m_”
STW
1
jeśli to nic nie znaczy, jak możesz przełączać się między _missionName i missionName, jak w powyższym przykładzie? Moja deklaracja wygląda następująco: Misja * aMission = [[Przydział misji] init]; aMission.missionName = @ "misja";
Atma
1
Jedna to zmienna instancji, a druga to właściwość. Nie możesz uzyskać dostępu do zmiennych instancji o składni takiej jak aMission.missionName, ponieważ ta składnia nie działa ze wskaźnikami.
Chuck
Zwróć również uwagę, że próbujesz operować na obiekcie misji, ale interfejs, który opublikowałeś z właściwością missionName, to MissionCell.
smorgan
2

Dotyczy to tylko konwencji nazewnictwa dotyczącej syntetyzowania właściwości.

Kiedy zsyntetyzujesz zmienne w pliku .m, Xcode automatycznie dostarczy Ci inteligencję _variable.

Dipak Narigara
źródło
1

Podkreślenie nie tylko umożliwia rozwiązywanie problemów z ivars bez uciekania się do używania self.member składni ale sprawia, że ​​twój kod jest bardziej czytelny, ponieważ wiesz, kiedy zmienna jest ivar (ze względu na jej przedrostek podkreślenia) lub argument składowy (bez podkreślenia ).

Przykład:

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

    if (image != nil) {
        // Display the passed image...
        [_imageView setImage: image];
    } else {
        // fall back on the default image...
        [_imageView setImage: _image];
    }
}
Jason Fuerstenberg
źródło
W tym przykładzie byłoby miło zobaczyć również porównanie użycia self.image (lub [self image]). Kiedy lepiej jest używać self.image, a kiedy _image?
Boeckm
2
@Boeckm: Ogólnie powinieneś użyć self.image, który uzyskuje dostęp do właściwości. Jedyny przypadek, w którym należy uzyskać dostęp do zmiennej instancji, _imagejest bezpośrednio w initmetodach i deallocmetodzie, wywołanie dowolnej innej metody może być ryzykowne (ponieważ obiekt jest w połowie zainicjowany lub zwolniony w połowie).
Peter Hosey
1

Wydaje się, że jest to element „nadrzędny” w przypadku pytań dotyczących self.variableName i _variablename. To, co rzuciło mnie na pętlę, to to, że w .h miałem:

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

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

Prowadzi to do tego, że self.variableName i _variableName są dwiema różnymi zmiennymi w .m. Potrzebowałem:

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

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

Następnie w klasie „.m” self.variableName i _variableName są równoważne.

Nadal nie jestem pewien, dlaczego wiele przykładów wciąż działa, nawet jeśli tak się nie dzieje.

Promień

RayInNoIL
źródło
0

zamiast podkreślenia możesz użyć nazwy zmiennej self lub możesz zsyntetyzować zmienną, aby użyć zmiennej lub wyjścia bez podkreślenia.

SARATH SASI
źródło
2
Jeśli potrzebujesz tylko zmienną w tej samej klasie tylko zadeklarować je w pliku .m się następnie pozwoli Ci zadzwonić bez siebie ani podkreślenia
Ansal Antony
0

W innych odpowiedziach brakuje tego, że używanie _variablezapobiega nieumyślnemu pisaniu variablei uzyskiwaniu dostępu do ivar, a nie (przypuszczalnie zamierzonej) właściwości.

Kompilator zmusi cię do użycia albo self.variablelub _variable. Stosowanie podkreśleń uniemożliwia wpisywanie variable, co zmniejsza liczbę błędów programisty.

- (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;

}
pkamb
źródło