Como um sublinhado na frente de uma variável em uma classe de objetivo-c de cacau funciona?


157

Eu já vi em alguns exemplos do iPhone que atributos usaram um sublinhado _ na frente da variável. Alguém sabe o que isso significa? Ou como funciona?

Um arquivo de interface que estou usando se parece com:

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

@property (nonatomic, retain) UILabel *missionName;

- (Mission *)mission;

Não sei exatamente o que faz o acima, mas quando tento definir o nome da missão como:

aMission.missionName = missionName;

Eu recebo o erro:

pedido de membro 'missionName' em algo que não seja uma estrutura ou união

Respostas:


97

Se você usar o prefixo de sublinhado para seus ivars (que nada mais é do que uma convenção comum, mas é útil), precisará fazer 1 coisa extra para que o acessador gerado automaticamente (para a propriedade) saiba qual ivar usar. Especificamente, no seu arquivo de implementação, você synthesizedeve ficar assim:

@synthesize missionName = _missionName;

Mais genericamente, isto é:

@synthesize propertyName = _ivarName;

78
com propriedades de síntese automática, isso não é mais necessário. O Xcode sintetiza um @property xxxx com um ivar chamado _xxxx nos bastidores. Arrumado.
LearnCocos2D

@ LearnCocos2D Oi! Um novato no iOS aqui e há algo que preciso esclarecer. Durante todo esse tempo, o que fiz foi declarar o propertyarquivo .he no arquivo .m eu acessá-lo selfdessa forma self.someProperty,. Esse é o caminho certo? Ou devo usar os ivars no código?
Isuru

definindo o ivar não executar o setter propriedade - você decide se é boa ideia ou não para cada caso em particular
LearnCocos2D

Noob pergunta: por que não usar os ivars diretamente? por que devo declarar um var separado para conter o ivar?
Allen

1
@ Allen, se eu entendi sua pergunta corretamente: O var separado que você está declarando é um ponteiro para a variável real. Isso é importante por alguns motivos (que eu conheço): primeiro, quando você passa um ponteiro para uma função, não está duplicando seu valor. Você está simplesmente dizendo à função onde encontrar o valor a ser usado. Isso ajuda a manter o seu baixo memória usada (e também ajuda com alloc e dealloc de memória, o que é importante na ausência de 'coleta de lixo', que você vai encontrar em Java)
David Sigley

18

É apenas uma convenção para facilitar a leitura, não faz nada de especial para o compilador. Você verá as pessoas usá-lo em variáveis ​​de instância privadas e nomes de métodos. Na verdade, a Apple recomenda não usar o sublinhado (se você não tomar cuidado, poderá substituir algo em sua superclasse), mas não se sinta mal por ignorar esse conselho. :)


19
Pelo que entendi, a Apple recomenda não usar o prefixo de sublinhado nos nomes dos métodos (eles reservam isso para si mesmos como uma convenção para métodos privados), mas eles não têm nenhuma recomendação sobre nomes de variáveis ​​de instância.
Kelan

9
@ Kelel De fato, a Apple recomenda : "Normalmente, você não deve acessar variáveis ​​de instância diretamente; em vez disso, deve usar métodos acessadores (você acessa variáveis ​​de instância diretamente nos métodos init e dealloc). Para ajudar a sinalizar isso, prefixe a instância nomes de variáveis ​​com sublinhado (_), por exemplo: \ @implementation MyClass {BOOL _showsTitle;} "
dmirkitanov

Na verdade, acho que a Apple não nos incentiva a fazê-lo, já que todos os seus próprios códigos de amostra na Biblioteca de desenvolvedores do iOS não têm o ( ) neles. A Apple também diz que a reservou, o que significa que a usa internamente para estruturas próprias como o UIKit etc. É por isso que não devemos usá-lo descuidadamente. Mas vejo que, no link que você forneceu @kelan. Na verdade, eles dizem no "histórico de revisões" que é "adequado" para usar ( ). Eu interpreto é como nós "podemos" usá-lo, se quisermos.
WYS

A documentação da Apple que diz para não usar o prefixo de sublinhado para nomes de métodos está aqui .
28515 ThomasW

9

O único propósito útil que eu vi é diferenciar entre variáveis ​​locais e variáveis ​​membros, conforme declarado acima, mas não é uma convenção necessária. Quando emparelhado com uma propriedade @, aumenta a verbosidade das declarações sintetizadas - @synthesize missionName = _missionName;e é feio em todos os lugares.

Em vez de usar o sublinhado, use apenas nomes descritivos de variáveis ​​em métodos que não entrem em conflito. Quando eles precisam entrar em conflito, o nome da variável no método deve sofrer um sublinhado, não a variável membro que pode ser usada por vários métodos . O único local comum em que isso é útil é em um setter ou em um método init. Além disso, tornará a declaração @synthesize mais concisa.

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

Edit: Com o recurso mais recente do compilador de auto-síntese, agora uso sublinhado para o ivar (nas raras ocasiões em que preciso usar um ivar para combinar com o que a auto-síntese faz.


É o contrário. A variável privada está sublinhada. a propriedade não. e quando sintetizá-los, você os junta.
Justin

É exatamente como eu descrevo, exceto que eu a chamei de "variável membro" em vez de "variável privada".
Peter DeWeese

Ai! Isso está causando problemas ... a auto-síntese criará o ivar _myString, o que significa que o seu setter não funcionará (porque, portanto, não poderá informar o seu ivar a partir do parâmetro method).
precisa saber é o seguinte

Correto, é por isso que adicionei a edição no final, quando a Apple adicionou a auto-síntese.
Peter DeWeese

5

Realmente não significa nada, é apenas uma convenção que algumas pessoas usam para diferenciar variáveis-membro de variáveis ​​locais.

Quanto ao erro, parece que aMission tem o tipo errado. Qual é a sua declaração?


É comum em IDE's com intellisense; isso fará com que suas variáveis ​​de membro / módulo / classe sejam exibidas no topo da lista. Outro prefixo comum é "m_"
STW

1
se isso não significa nada, como você pode alternar entre _missionName e missionName como no meu exemplo acima? Minha declaração se parece com: Missão * aMission = [[Missão alocada] init]; aMission.missionName = @ "uma missão";
417 Atma

1
Um é uma variável de instância e o outro é uma propriedade. Você não pode acessar variáveis ​​de instância com sintaxe como aMission.missionName, porque essa sintaxe não funciona com ponteiros.
Chuck

Além disso, observe que você está tentando operar em um objeto Mission, mas a interface que você publicou com a propriedade missionName é um MissionCell.
519 smorgan

2

Isso é apenas para a convenção de nomenclatura das propriedades de sintetizar.

Quando você sintetiza variáveis ​​no arquivo .m, o Xcode fornece automaticamente a inteligência _variável.


1

Ter um sublinhado não apenas torna possível resolver seus ivars sem recorrer ao uso da sintaxe self.member, mas também torna seu código mais legível, pois você sabe quando uma variável é um ivar (por causa de seu prefixo de sublinhado) ou um argumento de membro (sem sublinhado )

Exemplo:

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

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

Neste exemplo, seria bom ver uma comparação do uso de self.image (ou [autoimagem]) também. Quando é melhor usar self.image e quando é melhor usar _image?
Boeckm

2
@ Boeckm: Geralmente, você deve usar self.image, o que acessa a propriedade. O único momento em que você deve acessar a variável de instância _image, diretamente é dentro dos initmétodos e o deallocmétodo, ao chamar qualquer outro método, pode ser arriscado (já que o objeto é semi-inicializado ou desalocado).
Peter Hosey

1

Este parece ser o item "principal" para perguntas sobre self.variableName vs. _variablename. O que me impressionou foi que, no .h, eu tinha:

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

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

Isso faz com que self.variableName e _variableName sejam duas variáveis ​​distintas no .m. O que eu precisava era:

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

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

Em seguida, na classe '.m, self.variableName e _variableName são equivalentes.

O que ainda não estou claro é por que muitos exemplos ainda funcionam, mesmo que isso não seja feito.

Raio


0

em vez de sublinhado, você pode usar o nome self.variable ou sintetizar a variável para usar a variável ou saída sem sublinhado.


2
se você só precisa da variável da mesma classe, apenas a declare no arquivo .m, ela permitirá que você ligue sem si nem com o sublinhado
Ansal Antony

0

O que falta nas outras respostas é que o uso _variableimpede que você digite variablee acesse distraidamente o ivar em vez da propriedade (supostamente pretendida).

O compilador forçará você a usar um self.variableou _variable. O uso de sublinhados torna impossível digitar variable, o que reduz os erros do programador.

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

}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.