Por que o Objective-C não suporta métodos particulares?

123

Eu já vi várias estratégias para declarar métodos semi-privados no Objective-C , mas não parece haver uma maneira de criar um método verdadeiramente privado. Eu aceito isso. Mas, por que isso é assim? Todas as explicações que eu essencialmente digo, "você não pode fazê-lo, mas aqui está uma aproximação aproximada".

Há um número de palavras-chave aplicado a ivars(membros) que controlam o seu âmbito, por exemplo @private, @public, @protected. Por que isso não pode ser feito para métodos também? Parece algo que o tempo de execução deve suportar. Há uma filosofia subjacente que estou perdendo? Isso é deliberado?

Rob Jones
fonte
15
Boa pergunta, aliás.
bbum
5
Nas suas edições: sim, a imposição do tempo de execução seria muito cara, com muito pouco benefício. A imposição em tempo de compilação seria uma adição bem-vinda ao idioma e é bastante viável. Sim, isso pode ser contornado em tempo de execução, mas tudo bem. O programador não é um inimigo. O objetivo do escopo é ajudar a proteger o programador de erros.
Rob Napier
1
Você não pode "pular o tempo de execução" - o tempo de execução é o que despacha a mensagem.
Chuck
"Não haveria nenhuma maneira para que um método particular para curto-circuito esta verificação" Você quis dizer "contornar"? ;-)
Constantino Tsarouhas

Respostas:

103

A resposta é ... bem ... simples. Simplicidade e consistência, de fato.

O Objective-C é puramente dinâmico no momento do envio do método. Em particular, todo envio de método passa exatamente pelo mesmo ponto de resolução de método dinâmico que qualquer outro envio de método. No tempo de execução, toda implementação de método tem exatamente a mesma exposição e todas as APIs fornecidas pelo tempo de execução Objective-C que funcionam com métodos e seletores funcionam igualmente da mesma forma em todos os métodos.

Como muitos responderam (aqui e em outras perguntas), os métodos privados em tempo de compilação são suportados; se uma classe não declarar um método em sua interface disponível ao público, esse método também pode não existir no que diz respeito ao seu código. Em outras palavras, você pode obter todas as várias combinações de visibilidade desejadas no momento da compilação, organizando seu projeto adequadamente.

Há pouco benefício em duplicar a mesma funcionalidade no tempo de execução. Isso acrescentaria uma quantidade enorme de complexidade e sobrecarga. E mesmo com toda essa complexidade, ainda assim não impediria que todos, exceto o desenvolvedor mais casual, executassem seus métodos supostamente "privados".

EDIT: Uma das suposições que notei é que as mensagens privadas teriam que passar pelo tempo de execução, resultando em uma sobrecarga potencialmente grande. Isso é absolutamente verdade?

Sim, ele é. Não há razão para supor que o implementador de uma classe não queira usar todo o recurso Objective-C definido na implementação, e isso significa que o envio dinâmico deve acontecer. No entanto , não há nenhuma razão específica para que métodos privados não possam ser despachados por uma variante especial de objc_msgSend(), uma vez que o compilador saberia que eles eram privados; isto é, pode ser conseguido adicionando uma tabela de método somente privada à Classestrutura.

Não haveria maneira de um método privado causar um curto-circuito nessa verificação ou pular o tempo de execução?

Não foi possível pular o tempo de execução, mas o tempo de execução não precisaria necessariamente verificar os métodos privados.

Dito isto, não há razão para que terceiros não possam deliberadamente chamar objc_msgSendPrivate()um objeto, fora da implementação desse objeto, e algumas coisas (KVO, por exemplo) teriam que fazer isso. Com efeito, seria apenas uma convenção e pouco melhor na prática do que prefixar os seletores de métodos privados ou não mencioná-los no cabeçalho da interface.

Fazer isso, porém, minaria a pura natureza dinâmica da linguagem. Todo envio de método não passaria mais por um mecanismo de envio idêntico. Em vez disso, você ficaria em uma situação em que a maioria dos métodos se comporta de uma maneira e um pequeno punhado é apenas diferente.

Isso se estende além do tempo de execução, pois existem muitos mecanismos no cacau construídos sobre o dinamismo consistente do Objective-C. Por exemplo, tanto a codificação de valores-chave quanto a observação de valores-chave precisariam ser muito modificadas para dar suporte a métodos privados - provavelmente criando uma brecha explorável - ou métodos privados seriam incompatíveis.

bbum
fonte
49
+1 O Objective-C é (de certa forma) uma linguagem "big-boy" na qual se espera que os programadores exibam uma certa quantidade de disciplina, em vez de levar um tapa na mão pelo compilador / tempo de execução a cada passo. Eu acho isso refrescante. Para mim, restringir a visibilidade do método durante a compilação / vinculação é suficiente e preferível.
Quinn Taylor
11
Mais python sendo "obj-c-ic" :). Guido foi bastante proativo na manutenção do Python nos sistemas NeXT, incluindo a criação da 1ª versão do PyObjC. Assim, o ObjC influenciou um pouco o python .
bbum
3
+1 para o interessante conto histórico do ditador benevolente para toda a vida e seus cruzamentos com o mítico sistema NeXT.
Pokstad
5
Obrigado. Python, Java e Objective-C todos têm modelos de objetos de tempo de execução que são bastante parecidos com o [SmallTalk]. O Java foi diretamente derivado do Objective-C. Python seguiu um curso paralelo mais do que um de inspiração, mas Guido certamente estudou o NeXTSTEP de perto.
bbum
1
Exceto que eu não confio em sua licença e eu definitivamente preferiria clang ao gcc. Mas é um bom primeiro passo, com certeza.
Cthutu
18

O tempo de execução poderia suportar, mas o custo seria enorme. Todo seletor enviado deve ser verificado se é privado ou público para essa classe, ou cada classe precisará gerenciar duas tabelas de expedição separadas. Isso não é o mesmo para variáveis ​​de instância, porque esse nível de proteção é feito em tempo de compilação.

Além disso, o tempo de execução precisaria verificar se o remetente de uma mensagem privada é da mesma classe que o destinatário. Você também pode ignorar métodos privados; se a classe utilizada instanceMethodForSelector:, poderia retornar o retorno IMPa qualquer outra classe para que eles invocassem o método privado diretamente.

Métodos particulares não puderam ignorar o envio da mensagem. Considere o seguinte cenário:

  1. Uma classe AllPublictem um método de instância públicadoSomething

  2. Outra classe HasPrivatetem um método de instância privado também chamadodoSomething

  3. Você cria uma matriz que contém qualquer número de instâncias de ambos AllPubliceHasPrivate

  4. Você tem o seguinte loop:

    for (id anObject in myArray)
        [anObject doSomething];

    Se você executasse esse loop a partir de dentro AllPublic, o tempo de execução teria que interromper o envio doSomethingdas HasPrivateinstâncias, no entanto, esse loop seria utilizável se estivesse dentro da HasPrivateclasse.

dreamlax
fonte
1
Concordo que haveria um custo envolvido. Talvez não seja algo que você queira em um dispositivo portátil. Você pode apoiar o enorme qualificador caso contrário? O custo realmente importaria em um sistema desktop?
Rob Jones
1
Não pensei nisso, mas você pode estar certo. O Objective-C possui o envio de mensagens em tempo de execução versus a chamada do método de tempo de compilação em C ++, portanto, você estaria falando de uma verificação de tempo de compilação versus uma verificação em tempo de execução.
Remus Rusanu
Parece realmente estranho que o principal motivo para não dar suporte a métodos privados seja devido ao desempenho. Eu acho que a Apple simplesmente não achou que os métodos privados fossem muito necessários, independentemente de haver um impacto no desempenho. Caso contrário, eles deveriam ter escolhido um idioma diferente para sua empresa multibilionária.
Pokstad
1
Adicionar métodos privados não apenas complicaria a implementação do tempo de execução, mas também a semântica da linguagem. O controle de acesso é um conceito simples em linguagens estáticas, mas é uma má combinação para o envio dinâmico, que viria com muita sobrecarga conceitual e levaria a muitos, muitos "por que isso não funciona?" questões.
Jens Ayton
7
O que os métodos privados realmente comprariam para você? O Objective-C é, afinal, uma linguagem baseada em C. Portanto, se alguém tiver um código em execução no processo, poderá facilmente executá-lo independentemente de quão privada ou ofuscada possa ser uma API.
bbum
13

As respostas postadas até agora fazem um bom trabalho em responder à pergunta de uma perspectiva filosófica, por isso vou postular uma razão mais pragmática: o que seria ganho com a mudança da semântica da linguagem? É simples o suficiente para "ocultar" métodos privados de maneira eficaz. A título de exemplo, imagine que você tenha uma classe declarada em um arquivo de cabeçalho, assim:

@interface MyObject : NSObject {}
- (void) doSomething;
@end

Se você precisar de métodos "particulares", também poderá colocar isso no arquivo de implementação:

@interface MyObject (Private)
- (void) doSomeHelperThing;
@end

@implementation MyObject

- (void) doSomething
{
    // Do some stuff
    [self doSomeHelperThing];
    // Do some other stuff;
}

- (void) doSomeHelperThing
{
    // Do some helper stuff
}

@end

Claro, não é exatamente o mesmo que métodos privados C ++ / Java, mas é suficientemente próximo, por que alterar a semântica da linguagem, bem como o compilador, o tempo de execução, etc., para adicionar um recurso que já é emulado de maneira aceitável maneira? Como observado em outras respostas, a semântica de passagem de mensagens - e sua dependência da reflexão em tempo de execução - tornariam o manuseio de mensagens "privadas" não trivial.

mipadi
fonte
1
O problema dessa abordagem é que alguém pode substituir métodos privados (mesmo que sem o saber) quando subclasse sua classe. Suponho que essencialmente você precise escolher nomes que provavelmente não serão substituídos.
dreamlax
3
O que parece um hack para mim.
Rob Jones
1
@ Mike: A Apple declarou que os nomes dos métodos com sublinhados principais são explicitamente reservados apenas para a Apple: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… . A alternativa recomendada é prefixar com duas letras maiúsculas e depois um sublinhado.
dreamlax
8
Além disso, insira seu SSN após o método para que outros programadores saibam quem o escreveu. = D Namespaces, preciso deles !!!
Pokstad
2
Se você está preocupado com os métodos que definem seja substituído, você não adotaram adequadamente os níveis inerentes de verbosidade que Objective-C estimula ...
Kendall Helmstetter Gelner
7

A solução mais fácil é apenas declarar algumas funções C estáticas em suas classes de Objective-C. Eles só têm escopo de arquivo de acordo com as regras C para a palavra-chave estática e, por isso, só podem ser usadas pelos métodos dessa classe.

Sem barulho.

Cromulento
fonte
Eles precisam ser definidos fora da seção @implementation?
Rob Jones
@Rob - não, eles podem (e provavelmente devem ser) definidos dentro da implementação de suas classes.
Cromulent
6

Sim, isso pode ser feito sem afetar o tempo de execução, utilizando uma técnica já empregada pelo (s) compilador (es) para manipular C ++: manipulação de nomes.

Isso não foi feito porque não foi estabelecido que resolveria uma dificuldade considerável no espaço do problema de codificação que outras técnicas (por exemplo, prefixação ou sublinhado) são capazes de contornar suficientemente. Uau, você precisa de mais dor para superar hábitos arraigados.

Você pode contribuir com patches para clang ou gcc que adicionam métodos privados à sintaxe e geram nomes desconfigurados que somente eles reconhecem durante a compilação (e esquecem-se prontamente). Em seguida, outros membros da comunidade Objective-C poderiam determinar se realmente valia a pena ou não. É provável que seja mais rápido assim do que tentar convencer os desenvolvedores.

Huperniketes
fonte
2
A compilação de nomes em tempo de compilação é muito mais difícil do que se imagina, pois é necessário modificar todos os seletores de métodos, incluindo aqueles que podem aparecer em arquivos XIB e aqueles que podem ser passados ​​para coisas como NSSelectorFromString () e KeyValueCoding e amigos ...
bbum 29/01
1
Sim, estaria mais envolvido se você quisesse fornecer suporte para métodos privados em toda a cadeia de ferramentas. A tabela de símbolos do compilador precisaria ser armazenada e acessível através de alguma API da cadeia de ferramentas. Não seria a primeira vez que a Apple lançaria de forma incremental recursos para desenvolvedores que eram suficientemente importantes quando o tempo e a estabilidade permitiam. No entanto, no que diz respeito aos métodos privados: é questionável se é uma vitória suficiente para empreender o esforço. É ainda mais suspeito que eles devam ser acessíveis fora da própria classe por esses outros mecanismos.
Huperniketes
1
Como uma imposição apenas em tempo de compilação + trituração de nomes impede alguém de andar na lista de métodos de uma classe e encontrá-la lá?
dreamlax
Não seria. Minha resposta aborda apenas a pergunta feita pelo OP.
Huperniketes
Eu pensei em tentar adicionar métodos privados ao Clang, uma razão pela qual achei que os métodos privados seriam bons: eles poderiam ser chamados diretamente como funções c simples e, em seguida, você poderia ter todas as otimizações da função C usuais. Eu nunca me incomodei por causa do tempo e você já pode fazer isso usando c.
Nathan Day
4

Essencialmente, isso tem a ver com a forma de passagem de mensagens do Objective-C de chamadas de método. Qualquer mensagem pode ser enviada para qualquer objeto, e o objeto escolhe como responder à mensagem. Normalmente, ele responderá executando o método nomeado após a mensagem, mas também poderá responder de várias outras maneiras. Isso não torna os métodos privados completamente impossíveis - o Ruby faz isso com um sistema semelhante de passagem de mensagens -, mas os torna um pouco estranhos.

Até a implementação de métodos privados de Ruby é um pouco confusa para as pessoas devido à estranheza (você pode enviar ao objeto qualquer mensagem que desejar, exceto as desta lista !). Essencialmente, Ruby faz funcionar proibindo que métodos privados sejam chamados com um receptor explícito. No Objective-C, seria necessário ainda mais trabalho, pois o Objective-C não possui essa opção.

Mandril
fonte
1

É um problema com o ambiente de tempo de execução do Objective-C. Enquanto o C / C ++ é compilado em código de máquina ilegível, o Objective-C ainda mantém alguns atributos legíveis por humanos, como nomes de métodos como strings . Isso dá ao Objective-C a capacidade de executar recursos reflexivos .

EDIT: Ser uma linguagem reflexiva sem métodos estritos privados torna o Objective-C mais "pitônico", na medida em que você confia em outras pessoas que usam seu código, em vez de restringir quais métodos eles podem chamar. O uso de convenções de nomenclatura, como sublinhado duplo, visa ocultar seu código de um codificador de cliente casual, mas não impede que os codificadores precisem fazer um trabalho mais sério.

pokstad
fonte
E se os consumidores da sua biblioteca pudessem refletir seus métodos particulares, eles não poderiam chamá-los também?
precisa saber é o seguinte
Se eles sabem onde procurar, não é assim que as pessoas usam bibliotecas não documentadas no iPhone? O problema com o iPhone é que você corre o risco de não ter seu aplicativo aceito para usar qualquer API privada.
Pokstad
Se eles acessam métodos privados através da reflexão, obtêm o que merecem. Quaisquer erros não são sua culpa.
gnasher729
1

Existem duas respostas, dependendo da interpretação da pergunta.

O primeiro é ocultando a implementação do método da interface. Isso é usado, normalmente com uma categoria sem nome (por exemplo @interface Foo()). Isso permite que o objeto envie essas mensagens, mas não outras - embora ainda seja possível substituir acidentalmente (ou não).

A segunda resposta, supondo que se trata de desempenho e inclusão, é possível, mas como uma função C local. Se você quisesse um NSString *argmétodo 'private foo ( )', você o void MyClass_foo(MyClass *self, NSString *arg)chamaria como uma função C como MyClass_foo(self,arg). A sintaxe é diferente, mas atua com o tipo de características de desempenho sãs dos métodos privados do C ++.

Embora isso responda à pergunta, devo salientar que a categoria sem nome é de longe a maneira mais comum de Objective-C de fazer isso.

AlBlue
fonte
0

O Objective-C não suporta métodos privados porque não precisa deles.

No C ++, todo método deve estar visível na declaração da classe. Você não pode ter métodos que alguém incluindo o arquivo de cabeçalho não possa ver. Portanto, se você deseja que métodos que o código fora da sua implementação não deva usar, você não tem escolha, o compilador deve fornecer uma ferramenta para que você possa dizer que o método não deve ser usado, ou seja, a palavra-chave "private".

No Objective-C, você pode ter métodos que não estão no arquivo de cabeçalho. Portanto, você alcança o mesmo objetivo com muita facilidade, não adicionando o método ao arquivo de cabeçalho. Não há necessidade de métodos particulares. O Objective-C também tem a vantagem de que você não precisa recompilar todos os usuários de uma classe porque alterou os métodos privados.

Por exemplo, variáveis ​​que você costumava declarar no arquivo de cabeçalho (não mais), @private, @public e @protected estão disponíveis.

gnasher729
fonte
0

Uma resposta que falta aqui é: porque os métodos privados são uma má ideia do ponto de vista da capacidade de evoluir. Pode parecer uma boa idéia tornar um método privado ao escrevê-lo, mas é uma forma de ligação antecipada. O contexto pode mudar e um usuário posterior pode querer usar uma implementação diferente. Um pouco provocador: "Os desenvolvedores ágeis não usam métodos privados"

De certa forma, assim como o Smalltalk, o Objective-C é para programadores adultos. Valorizamos saber o que o desenvolvedor original assumiu que a interface deva ser e assumimos a responsabilidade de lidar com as consequências, se precisarmos alterar a implementação. Então, sim, é filosofia, não implementação.

Stephan Eggermont
fonte
Eu votei para cima porque não merece a votação para baixo. Há, como Stephan diz, um argumento de que métodos "privados" não são necessários, e é semelhante aos argumentos sobre tipagem dinâmica versus estática, pois, qualquer que seja sua conclusão preferida, ambos os lados têm razão.
Alastair