Как работает подчеркивание перед переменной в классе target-c какао?


157

Я видел в нескольких примерах iPhone, что атрибуты используют подчеркивание _ перед переменной. Кто-нибудь знает что это значит? Или как это работает?

Файл интерфейса, который я использую, выглядит следующим образом:

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

@property (nonatomic, retain) UILabel *missionName;

- (Mission *)mission;

Я не уверен точно, что делает вышеупомянутое, но когда я пытаюсь установить название миссии как:

aMission.missionName = missionName;

Я получаю ошибку:

запрос на членство 'missionName' в чем-то, что не является структурой или объединением

Ответы:


97

Если вы используете префикс подчеркивания для своего ivars (который является не чем иным, как обычным соглашением, но полезным), то вам нужно сделать 1 дополнительную вещь, чтобы автоматически сгенерированный аксессор (для свойства) знал, какой ivar использовать. В частности, в вашем файле реализации вы synthesizeдолжны выглядеть так:

@synthesize missionName = _missionName;

В более общем смысле это:

@synthesize propertyName = _ivarName;

78
с автоматически синтезирующими свойствами это больше не нужно. Xcode синтезирует @property xxxx с иваром с именем _xxxx за кулисами. Ухоженная.
LearnCocos2D

@ LearnCocos2D Привет! Новичок в iOS здесь, и есть кое-что, что мне нужно уточнить. За все это время я объявил propertyв файле .h и в файле .m fie доступ к нему selfследующим образом self.someProperty. Это правильный путь? Или я должен использовать ivars в коде?
Исуру

установка ivar не запускает установщик свойств - вы сами решаете, будет ли это хорошей идеей или нет для каждого конкретного случая
LearnCocos2D

Вопрос нуба: почему бы не использовать ивары напрямую? почему я должен объявить отдельную переменную для хранения ивара?
Аллен

1
@ Аллен, если я правильно понимаю твой вопрос: отдельная переменная, которую ты объявляешь, является указателем на фактическую переменную. Это важно по нескольким причинам (о которых я знаю). Во-первых, когда вы передаете указатель в функцию, вы не дублируете его значение. Вы просто указываете функции, где найти значение для использования. Это помогает поддерживать низкий уровень используемой памяти (а также помогает с выделением и освобождением памяти, что важно при отсутствии «сборки мусора», которую вы найдете в Java)
Дэвид Сигли

18

Это просто соглашение по удобочитаемости, оно не делает ничего особенного для компилятора. Вы увидите, как люди используют его для личных переменных экземпляра и имен методов. Apple фактически рекомендует не использовать подчеркивание (если вы не будете осторожны, вы можете переопределить что-то в своем суперклассе), но вы не должны расстраиваться из-за игнорирования этого совета. :)


19
Из того, что я понимаю, Apple рекомендует не использовать префикс подчеркивания в именах методов (они резервируют его для себя как соглашение для частных методов), но у них нет таких рекомендаций относительно имен переменных экземпляра.
Келан

9
@Kelan На самом деле Apple рекомендует делать следующее : «Обычно вам не следует обращаться к переменным экземпляра напрямую, вместо этого вы должны использовать методы доступа (вы обращаетесь к переменным экземпляра непосредственно в методах init и dealloc). Чтобы помочь сигнализировать об этом, добавьте префикс экземпляра имена переменных с подчеркиванием (_), например: \ @implementation MyClass {BOOL _showsTitle;} "
dmirkitanov

Я на самом деле не думаю, что Apple поощряет нас делать это, поскольку во всех их собственных примерах кода в библиотеке разработчика iOS нет ( ). Apple также говорит, что они зарезервировали ее, что должно означать, что они используют ее для своих собственных сред, таких как UIKit и т. Д. Именно поэтому мы не должны небрежно использовать ее. Но я вижу это в ссылке, которую вы предоставили @kelan. Они на самом деле говорят в «истории изменений», что это «подходит» для использования ( ). Я понимаю, что мы можем использовать его, если захотим.
WYS

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

9

Единственная полезная цель, которую я видел, состоит в том, чтобы различать локальные переменные и переменные-члены, как указано выше, но это не является необходимым соглашением. В сочетании с @property, это увеличивает многословность операторов синтеза - @synthesize missionName = _missionName;и везде безобразно.

Вместо использования подчеркивания, просто используйте описательные имена переменных внутри методов, которые не конфликтуют. Когда они должны конфликтовать, имя переменной в методе должно иметь подчеркивание, а не переменную-член, которая может использоваться несколькими методами . Единственное общее место, где это полезно, находится в установщике или в методе init. Кроме того, это сделает оператор @synthesize более лаконичным.

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

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


Это наоборот. закрытая переменная подчеркнута. собственности нет. и когда они синтезируют их, вы соединяете их.
Джастин

Это именно то, что я описываю, за исключением того, что я назвал это «переменной-членом» вместо «частной переменной».
Питер ДеВиз

Ой! Это вызывает проблемы ... автоматический синтез создаст ivar _myString, что означает, что ваш установщик не будет работать (потому что тогда он не сможет отличить ваш ivar от параметра метода).
Geowar

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

5

На самом деле это ничего не значит, это просто соглашение, которое некоторые люди используют для дифференциации переменных-членов от локальных переменных.

Что касается ошибки, похоже, что aMission имеет неправильный тип. Каково это его объявление?


Это часто встречается в IDE с intellisense; это сделает ваши переменные member / module / class показанными вверху списка. Еще один распространенный префикс "m_"
STW

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

1
Одна - это переменная экземпляра, а другая - свойство. Вы не можете получить доступ к переменным экземпляра с помощью синтаксиса, такого как aMission.missionName, потому что этот синтаксис не работает с указателями.
Чак

Также обратите внимание, что вы пытаетесь работать с объектом Mission, но интерфейс, который вы разместили со свойством missionName, - это MissionCell.
Сморган

2

Это только для соглашения об именовании свойств синтеза.

Когда вы синтезируете переменные в файле .m, Xcode автоматически предоставит вам _variable интеллект.


1

Наличие подчеркивания не только позволяет разрешать ваши ивары, не прибегая к использованию синтаксиса self.member, но и делает ваш код более читабельным, поскольку вы знаете, когда переменная является иваром (из-за префикса подчеркивания) или аргументом-членом (без подчеркивания) ).

Пример:

- (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 и _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

вместо подчеркивания вы можете использовать имя self.variable или вы можете синтезировать переменную, чтобы использовать переменную или выход без подчеркивания.


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.