Як працює підкреслення перед змінною в класі какао-об'єктивного класу?


157

Я бачив на кількох прикладах iPhone, що атрибути використовували підкреслення _ перед змінною. Хтось знає, що це означає? Або як це працює?

Файл інтерфейсу, який я використовую, виглядає так:

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

@property (nonatomic, retain) UILabel *missionName;

- (Mission *)mission;

Я не впевнений, що саме робить вище, але коли я намагаюся встановити ім'я місії на зразок:

aMission.missionName = missionName;

Я отримую помилку:

запит на членство "missionName" у чомусь не структурі чи об'єднанні

Відповіді:


97

Якщо ви використовуєте префікс підкреслення для своїх ivars (що є не що інше, як звичайна умова, але корисна), тоді вам потрібно зробити ще одну додаткову річ, щоб автоматично створений аксесуар (для власності) знав, який ivar використовувати. Зокрема, у вашому файлі реалізації synthesizeмає виглядати так:

@synthesize missionName = _missionName;

Більш загально, це:

@synthesize propertyName = _ivarName;

78
з властивостями автоматичного синтезування це вже не потрібно. Xcode синтезує @property xxxx з ivar на ім'я _xxxx поза кадром. Акуратний.
LearnCocos2D

@ LearnCocos2D Привіт! Новачок в iOS тут і є щось, що мені потрібно з’ясувати. За весь цей час , що я був оголосити propertyв файлі .h і .m в тьху I доступ до неї , використовуючи selfяк так, self.someProperty. Це правильний шлях? Або я повинен використовувати ivars в коді?
Ісуру

встановлення ivar не запускає налаштування власності - ви вирішите, чи це гарна ідея чи ні для кожного конкретного випадку
LearnCocos2D

Питання Noob: чому б не використовувати безпосередньо ivars? чому я повинен оголосити окремий var, щоб утримувати ivar?
Аллен

1
@Allen, якщо я правильно розумію ваше запитання: Окремий вар, який ви заявляєте, - це вказівник на фактичну змінну. Це важливо з кількох причин (про які я знаю) По-перше, коли ви передаєте вказівник на функцію, ви не дублюєте її значення. Ви просто вказуєте функцію, де знайти значення для використання. Це допомагає зберегти використану пам’ять низькою (а також допомагає приділити і розподілити пам'ять, що важливо за відсутності "сміття", яке ви знайдете на Java)
Девід Сіглі

18

Це просто умова для читабельності, вона не робить нічого особливого для компілятора. Ви побачите, що люди використовують його у приватних змінних екземплярів та іменах методів. Apple насправді рекомендує не використовувати підкреслення (якщо ви не обережні, можете перекрити щось у своєму суперкласі), але вам не слід погано ігнорувати цю пораду. :)


19
З того, що я розумію, Apple рекомендує не використовувати префікс підкреслення для імен методів (вони залишають за собою умову для приватних методів), але не мають такої рекомендації щодо імен змінних екземплярів.
Келан

9
@Kelan Насправді Apple рекомендує це робити : "Зазвичай, ви не повинні отримувати доступ до змінних екземплярів безпосередньо, замість цього слід використовувати методи accessor (ви можете використовувати змінні екземпляра безпосередньо в методах init і dealloc). Щоб допомогти сигналізувати про це, примірник префіксу назви змінних із підкресленням (_), наприклад: \ @implementation MyClass {BOOL _showsTitle;} "
dmirkitanov

Я насправді не думаю, що Apple заохочує нас до цього, оскільки всі власні зразкові коди в бібліотеці розробників iOS не містять ( ) у них. Apple також каже, що вони зарезервували це, що повинно означати, що вони використовують його внутрішньо для власних рамок, таких як UIKit і т. Д. Тому ми не повинні недбало ними користуватися. Але я бачу це у посиланні, яке ви надали @kelan. Вони фактично в "історії перегляду" говорять, що це "придатне" для використання ( ). Я тлумачу так, як ми "можемо" використовувати його, якщо хочемо.
WYS

Документація Apple, яка говорить не використовувати підкреслення префікс для імен методів є тут .
ThomasW

9

Єдина корисна мета, яку я бачив, - це розмежувати локальні та членські змінні, як зазначено вище, але це не є необхідною умовою. У поєднанні з @ властивістю він збільшує багатослівність синтезування висловлювань - @synthesize missionName = _missionName;і скрізь некрасивий.

Замість того, щоб використовувати підкреслення, просто використовуйте описові назви змінних в межах методів, які не суперечать. Коли вони повинні суперечити, ім'я змінної в методі має зазнавати підкреслення, а не змінну члена, яка може використовуватися кількома методами . Єдине загальне місце, яке це корисно, - це сетер або метод init. Крім того, це зробить заяву @synthesize більш стислою.

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

Редагувати: За допомогою останньої функції автоматичного синтезу компілятора я зараз використовую підкреслення для ivar (за рідкісних випадків, коли мені потрібно використовувати ivar, щоб відповідати тому, що робить автоматичний синтез.


Це навпаки. приватна змінна підкреслена. майно ні. і синтезуючи їх сироваткою, ви їх з’єднуєте.
Джастін

Це саме так, як я описую, за винятком того, що я назвав його "змінною члена" замість "приватної змінної".
Peter DeWeese

Ой! Це задає проблеми ... автоматичний синтез зробить ivar _myString, що означає, що ваш сетер не буде працювати (тому що він не зможе повідомити ваш ivar з параметра методу).
geowar

Правильно, тому я додав правки наприкінці, коли яблуко додало автоматичний синтез.
Пітер ДеВіз

5

Це насправді нічого не означає, це лише умова, яку деякі використовують для диференціації змінних членів від локальних змінних.

Що стосується помилки, то вона здається, що aMission має неправильний тип. Що це його декларація?


Це поширене в IDE з інтелігенцією; це призведе до відображення змінних вашого члена / модуля / класу у верхній частині списку. Ще одна поширена превікса - "m_"
STW

1
якщо це нічого не означає, як ви можете перемикатися вперед і назад між _missionName та taskName, як у моєму прикладі вище? Моя декларація виглядає так: Mission * aMission = [[Mission alloc] init]; aMission.missionName = @ "місія";
Атма

1
Один є змінною екземпляра, а інший - властивістю. Ви не можете отримати доступ до змінних екземплярів із синтаксисом, як aMission.missionName, оскільки цей синтаксис не працює з покажчиками.
Чак

Також зауважте, що ви намагаєтесь працювати над об’єктом Mission, але інтерфейс, який ви розмістили з властивістюsionName, є MissionCell.
smorgan

2

Це стосується лише узгодження іменних властивостей синтезу.

Коли ви синтезуєте змінні у файлі .m, Xcode автоматично надасть вам змінний інтелект.


1

Наявність підкреслення не тільки дає змогу вирішити ваші ivars, не вдаючись до використання синтаксису self.member, але це зробить ваш код більш читабельним, оскільки ви знаєте, коли змінною є ivar (через його префікс підкреслення) або аргумент члена (немає підкреслення ).

Приклад:

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

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

У цьому прикладі було б непогано побачити порівняння використання self.image (або [self image]). Коли краще використовувати self.image і коли краще використовувати _image?
Boeckm

2
@Boeckm: Як правило, ви повинні використовувати self.image, яке має доступ до властивості. Єдиний раз, коли вам слід отримати доступ до змінної екземпляра _image, безпосередньо знаходиться в межах initметодів і deallocметоду, коли виклик будь-якого іншого методу може бути ризикованим (оскільки об'єкт є напівініціалізованим або напіврозділеним).
Пітер Хосей

1

Здається, це "головний" пункт для запитань про self.variableName vs. _variablename. Що мене кинуло за цикл, це те, що в .h, у мене було:

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

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

Це призводить до того, що self.variableName та _variableName є двома різними змінними у .m. Що мені потрібно було:

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

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

Тоді у класі '.m, self.variableName та _variableName є рівнозначними.

Я все ще не зрозумів, чому багато прикладів все ще працюють, навіть якщо це не робиться.

Промінь


0

замість підкреслення ви можете використовувати ім'я самостійної змінної або ви можете синтезувати змінну, щоб використовувати змінну або розетку без підкреслення.


2
якщо вам потрібна лише змінна в тому ж класі, просто оголосіть її у самому файлі .m, тоді вона дозволить вам телефонувати без себе, ані підкреслення
Ansal Antony

0

Від інших відповідей пропущено те, що використання _variableне дозволяє навмисно вводити variableта отримувати доступ до ivar, а не до (імовірно призначеного) властивості.

Компілятор змусить вас використовувати self.variableабо _variable. Використання підкреслень унеможливлює набір тексту variable, що зменшує помилки програміста.

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

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.