Notação de pontos vs. notação de mensagens para propriedades declaradas

Agora temos a notação “ponto” para propriedades. Eu já vi várias idas e vindas sobre os méritos da notação de ponto versus notação de mensagem. Para manter as respostas imaculadas, não vou responder de nenhuma maneira na pergunta.

Qual é o seu pensamento sobre notação de ponto versus notação de mensagem para access à propriedade?

Por favor, tente manter o foco em Objective-C – meu único viés que vou colocar é que Objective-C é Objective-C, então sua preferência por Java ou JavaScript não é válida.

Comentários válidos têm a ver com questões técnicas (ordem de operação, precedência do casting, desempenho, etc), clareza (estrutura vs. natureza do object, tanto pró como contra!), Sucessão etc.

Note, eu sou da escola de rigorosa qualidade e legibilidade no código, tendo trabalhado em grandes projetos onde convenção de código e qualidade é primordial (a escrita uma vez lida mil vezes o paradigma).

   

Não use ponto para comportamento. Use ponto para acessar ou definir atributos como coisas, tipicamente atributos declarados como propriedades.

x = foo.name; // good foo.age = 42; // good y = x.retain; // bad k.release; // compiler should warn, but some don't. Oops. v.lockFocusIfCanDraw; /// ooh... no. bad bad bad 

Para pessoas novas no Objective-C, eu recomendaria não usar o ponto para nada além de coisas declaradas como @property. Depois de sentir a linguagem, faça o que parece certo.

Por exemplo, acho o seguinte perfeitamente natural:

 k = anArray.count; for (NSView *v in myView.subviews) { ... }; 

Você pode esperar que o analisador estático cresça a capacidade de permitir que você verifique se o ponto está sendo usado apenas para determinados padrões ou não para certos outros padrões.

Deixe-me começar dizendo que comecei a programar em Visual / Real Basic, depois mudei para Java, então estou bastante acostumado a pontuar a syntax. No entanto, quando eu finalmente mudei para o Objective-C e me acostumei com colchetes, vi a introdução do Objective-C 2.0 e sua syntax de pontos, percebi que realmente não gosto disso. (para outras linguagens, tudo bem, porque é assim que elas rolam).

Eu tenho três cortes principais com a syntax do ponto em Objective-C:

Carne # 1: Não fica claro por que você pode estar recebendo erros. Por exemplo, se eu tiver a linha:

something.frame.origin.x = 42;

Então, recebo um erro do compilador, porque something é um object e você não pode usar estruturas de um object como o lvalue de uma expressão. No entanto, se eu tiver:

something.frame.origin.x = 42;

Em seguida, isso compila muito bem, porque something é uma estrutura em si que tem um membro NSRect e eu posso usá-lo como um lvalue.

Se eu estivesse adotando esse código, precisaria gastar algum tempo tentando descobrir o que é something . É uma estrutura? É um object? No entanto, quando usamos a syntax de colchetes, é muito mais claro:

[something setFrame:newFrame];

Neste caso, não há absolutamente nenhuma ambigüidade se something é um object ou não. A introdução da ambigüidade é minha carne # 1.

Carne # 2: Em C, a syntax de ponto é usada para acessar membros de estruturas, não para methods de chamada. Os programadores podem sobrescrever os setFoo: e foo de um object, e ainda assim acessá-lo via setFoo: . Na minha opinião, quando vejo expressões usando a syntax de pontos, espero que sejam uma simples atribuição a um ivar. Isso não é sempre o caso. Considere um object controlador que media uma matriz e uma visualização de tabela. Se eu chamo myController.contentArray = newArray; , Eu esperaria que ele estivesse substituindo o array antigo pelo novo array. No entanto, o programador original pode ter substituído setContentArray: para não apenas definir a matriz, mas também recarregar a tableview. Da linha, não há indicação desse comportamento. Se eu fosse ver [myController setContentArray:newArray]; , então eu pensaria “Aha, um método. Eu preciso ir ver a definição deste método apenas para ter certeza que eu sei o que está fazendo.”

Então eu acho que meu resumo do Beef # 2 é que você pode replace o significado da syntax do ponto pelo código customizado.

Carne # 3: Eu acho que parece ruim. Como um programador Objective-C, eu estou totalmente acostumada a colocar a syntax entre parênteses, então para estar lendo e ver linhas e linhas de colchetes bonitos e então ser subitamente quebrado com foo.name = newName; foo.size = newSize; foo.name = newName; foo.size = newSize; etc é um pouco perturbador para mim. Eu percebo que algumas coisas requerem uma syntax de pontos (estruturas C), mas essa é a única vez que eu as uso.

Claro, se você está escrevendo código para si mesmo, então use o que você está confortável com. Mas se você está escrevendo código que você está pensando em abrir o código, ou está escrevendo algo que você não espera manter para sempre, então eu gostaria de encorajar o uso da syntax de colchetes. Esta é, evidentemente, apenas a minha opinião.

Postagem recente no blog contra a syntax do ponto: http://weblog.bignerdranch.com/?p=83

Refutação ao post acima: http://eschatologist.net/blog/?p=226 (com artigo original em favor da syntax do ponto: http://eschatologist.net/blog/?p=160 )

Eu sou um novo desenvolvedor Cocoa / Objective-C, e minha opinião é a seguinte:

Eu mantenho a notação de mensagens, mesmo que eu tenha começado com Obj-C 2.0, e mesmo que a notação de ponto seja mais familiar (Java é minha primeira língua.) Minha razão para isso é bem simples: eu ainda não entendi exatamente porque eles adicionaram a notação de ponto ao idioma. Para mim, parece uma adição desnecessária e “impura”. Embora se alguém puder explicar como isso beneficia a linguagem, ficaria feliz em ouvi-lo.

No entanto, considero isso uma escolha estilística, e não acho que haja um caminho certo ou errado, desde que seja consistente e legível , assim como com qualquer outra escolha estilística (como colocar sua chave de abertura na mesma linha o header do método ou a próxima linha).

A notação de ponto C-objective é um açúcar sintático que é traduzido para a passagem normal de mensagens, portanto, sob o capô nada muda e não faz diferença no tempo de execução. A notação de ponto não é absolutamente mais rápida que a passagem de mensagens.

Depois disso, precisamos de um pequeno preâmbulo, aqui estão os prós e contras vistos por mim:

Prós e contras de notação de ponto

  • pros

    • legibilidade: a notação de pontos é mais fácil de ler do que os colchetes com massagens aninhadas passando
    • Ele simplifica a interação com atributos e propriedades: usando notação de ponto para propriedades e notação de mensagem para methods você pode obter separação de estado e comportamento no nível de syntax
    • É possível usar o operador de atribuição composta (1) .
    • Usando o @property ea notação de ponto, o compilador faz muito trabalho para você, ele pode gerar código para um bom gerenciamento de memory ao obter e configurar a propriedade; É por isso que a notação de pontos é sugerida pelos próprios guias oficiais da Apple.
  • contras

    • A notação de pontos é permitida apenas para access a uma propriedade @property declarada
    • Como o Objective-C é uma camada acima do padrão C (extensão de linguagem), a notação de ponto não deixa claro se a entidade acessada é um object ou uma estrutura. Muitas vezes, parece que você está acessando propriedades de uma estrutura.
    • chamando um método com a notação de ponto você perde vantagens de legibilidade de parâmetros nomeados
    • quando notação de mensagem mista e notação de ponto parece que você está codificando em duas linguagens diferentes

Exemplos de código:

(1) Exemplo de código de uso do operador composto:

 //Use of compound operator on a property of an object anObject.var += 1; //This is not possible with standard message notation [anObject setVar:[anObject var] + 1]; 

Usar o estilo de uma linguagem, consistente com a própria linguagem, é o melhor conselho aqui. No entanto, este não é um caso de escrever código funcional em um sistema OO (ou vice-versa) e a notação de ponto é parte da syntax no Objective-C 2.0.

Qualquer sistema pode ser mal utilizado. A existência do pré-processador em todas as linguagens baseadas em C é suficiente para fazer coisas bem estranhas; basta olhar para o Concurso C Ofuscado se você precisar ver exatamente o quão estranho ele pode ser. Isso significa que o pré-processador é automaticamente ruim e que você nunca deve usá-lo?

Usando a syntax de ponto para acessar propriedades, que foram definidas como tal na interface, está aberto a abusos. A existência de abuso em potentia não deve necessariamente ser o argumento contra ele.

O access à propriedade pode ter efeitos colaterais. Isso é ortogonal à syntax usada para adquirir essa propriedade. CoreData, delegação, propriedades dinâmicas (first + last = full) irão necessariamente fazer algum trabalho embaixo das capas. Mas isso seria confundir ‘variables ​​de instância’ com ‘propriedades’ de um object. Não há razão para que as propriedades precisem necessariamente ser armazenadas como estão, especialmente se puderem ser calculadas (por exemplo, comprimento de uma String, por exemplo). Então, se você usar foo.fullName ou [foo fullName], ainda haverá avaliação dinâmica.

Por fim, o comportamento da propriedade (quando usado como um lvalue ) é definido pelo próprio object, como se uma cópia é tirada ou se é retida. Isso torna mais fácil alterar o comportamento mais tarde – na própria definição da propriedade – em vez de precisar reimplementar os methods. Isso aumenta a flexibilidade da abordagem, com a conseqüente probabilidade de ocorrerem menos erros (de implementação). Ainda existe a possibilidade de escolher o método errado (ou seja, copiar em vez de reter), mas isso é um problema de arquitetura, e não de implementação.

Em última análise, resume-se à pergunta “parece uma estrutura”. Este é provavelmente o principal diferencial nos debates até agora; Se você tem uma estrutura, ela funciona de maneira diferente do que se você tivesse um object. Mas isso sempre foi verdade; você não pode enviar uma mensagem struct, e você precisa saber se ela é baseada em pilha ou referência / malloc. Já existem modelos mentais que diferem em termos de uso ([[CGRect alloc] init] ou struct CGRect?). Eles nunca foram unificados em termos de comportamento; você precisa saber com o que está lidando em cada caso. É improvável que adicionar denotação de propriedade para objects confunda qualquer programador que saiba quais são seus tipos de dados; e se não, eles têm problemas maiores.

Quanto à consistência; (Objetivo-) C é inconsistente em si mesmo. = é usado tanto para atribuição quanto para igualdade, com base na posição lexical no código-fonte. * é usado para pointers e multiplicação. BOOLs são chars, não bytes (ou outro valor inteiro), apesar de YES e NO serem 1 e 0, respectivamente. Consistência ou pureza não é o que a linguagem foi projetada; foi sobre fazer as coisas.

Então, se você não quiser usá-lo, não o use. Faça isso de uma maneira diferente. Se você quiser usá-lo e entender, tudo bem se você usá-lo. Outras linguagens lidam com os conceitos de estruturas de dados genéricas (mapas / estruturas) e tipos de objects (com propriedades), geralmente usando a mesma syntax para ambas, apesar do fato de que uma é meramente uma estrutura de dados e a outra é um object rico. Os programadores em Objective-C devem ter uma capacidade equivalente para lidar com todos os estilos de programação, mesmo que não seja o seu preferido.

Eu uso para propriedades porque

 for ( Person *person in group.people){ ... } 

é um pouco mais fácil de ler do que

 for ( Person *person in [group people]){ ... } 

no segundo caso, a legibilidade é interrompida colocando o seu cérebro no modo de envio de mensagens, enquanto no primeiro caso fica claro que você está acessando a propriedade people do object do grupo.

Também vou usá-lo ao modificar uma coleção, por exemplo:

 [group.people addObject:another_person]; 

é um pouco mais legível do que

 [[group people] addObject:another_person]; 

A ênfase neste caso deve estar na ação de adicionar um object ao array em vez de encadear duas mensagens.

Eu cresci principalmente na era do Objective-C 2.0, e eu prefiro a notação de ponto. Para mim, permite a simplificação do código, em vez de ter colchetes extras, posso usar apenas um ponto.

Eu também gosto da syntax de ponto porque me faz realmente sentir que estou acessando uma propriedade do object, em vez de apenas enviar uma mensagem (é claro que a syntax de pontos realmente se traduz em envio de mensagens, mas em nome das aparências , o ponto parece diferente). Em vez de “chamar um getter” pela antiga syntax, parece que estou obtendo algo útil diretamente do object.

Parte do debate em torno disso diz respeito a “Mas já temos a syntax de pontos, e é por structs !”. E isso é verdade. Mas (e mais uma vez, isso é apenas psicológico) basicamente parece o mesmo para mim. Acessar uma propriedade de um object usando a syntax de ponto é o mesmo que acessar um membro de uma estrutura, que é mais ou menos o efeito desejado (na minha opinião).

**** Edit: Como o álbum apontou, você também pode usar a syntax de ponto para chamar qualquer método em um object (eu não sabia disso). Então, eu vou dizer que minha opinião sobre a syntax de pontos é apenas para lidar com as propriedades de um object, não com o envio de mensagens diárias **

Eu prefiro muito mais a syntax de mensagens … mas só porque é isso que eu aprendi. Considerando muitas das minhas classs e o que não estão no estilo Objective-C 1.0, eu não gostaria de mixá-las. Eu não tenho nenhuma razão real além de “o que eu estou acostumado” por não usar a syntax do ponto … EXCETO para isso, isso me deixa INSANO

 [myInstance.methodThatReturnsAnObject sendAMessageToIt] 

Eu não sei porque, mas isso realmente me enfurece, sem uma boa razão. Eu só acho que fazendo

 [[myInstance methodThatReturnsAnObject] sendAMessageToIt] 

é mais legível. Mas para cada um o seu!

Honestamente, acho que se trata de uma questão de estilo. Eu pessoalmente sou contra a syntax do ponto (especialmente depois de descobrir que você pode usá-lo para chamadas de método e não apenas ler / escrever variables). No entanto, se você for usá-lo, eu recomendo fortemente não usá-lo para nada além de acessar e alterar variables.

Uma das principais vantagens da programação orientada a objects é que não há access direto ao estado interno dos objects.

Parece que a syntax de ponto é uma tentativa de fazer com que pareça que o estado está sendo acessado diretamente. Mas, na verdade, é apenas açúcar sintático sobre os comportamentos – foo e – setFoo:. Eu prefiro chamar uma pá de pá. A syntax de pontos ajuda a legibilidade na medida em que o código é mais sucinto, mas não ajuda a compreender, porque não ter em mente que você está realmente chamando -foo e -setFoo: pode significar problemas.

Os accessres sintetizados parecem ser uma tentativa de facilitar a gravação de objects nos quais o estado é acessado diretamente. Acredito que isso incentiva exatamente o tipo de projeto de programa que a programação orientada a objects foi criada para evitar.

Em suma, eu preferiria pontilhar a syntax e as propriedades nunca foram introduzidas. Eu costumava dizer às pessoas que o ObjC tem algumas extensões limpas para o C para torná-lo mais parecido com o Smalltalk, e eu não acho que isso seja verdade mais.

Na minha opinião, a syntax do ponto torna o Objective-C menos do tipo Smalltalk. Ele pode tornar o código mais simples, mas adiciona ambigüidade. É uma struct , union ou object?

Muitas pessoas parecem estar misturando ‘propriedades’ com ‘variables ​​de instância’. O ponto das propriedades é tornar possível modificar o object sem ter que conhecer seus componentes internos, eu acho. A variável de instância é, na maioria das vezes, a ‘implementação’ de uma propriedade (que por sua vez é a ‘interface’), mas nem sempre: às vezes, uma propriedade não corresponde a um ivar e calcula um valor de retorno ‘ no vôo’.

É por isso que acredito que a ideia de que “a syntax dos pontos faz você pensar que está acessando a variável de forma confusa” está errada. Ponto ou parêntese, você não deve fazer suposições sobre os internos: é uma propriedade, não um ivar.

Eu acho que eu poderia mudar para mensagens em vez de notação de ponto porque na minha cabeça object.instanceVar é apenas instanceVar que pertence ao object , para mim não parece nada como uma chamada de método , o que é, então pode haver coisas acontecendo em seu acessador e se você usa instanceVar ou self.instanceVar poderia ter muito mais uma diferença do que simplesmente implícito versus explícito. Apenas meu 2 ¢.

A notação de ponto tenta fazer com que as mensagens pareçam accesss a um membro de uma estrutura, o que elas não são. Pode funcionar bem em alguns casos. Mas logo alguém virá algo como isto:

 NSView *myView = ...; myView.frame.size.width = newWidth; 

Parece bem. Mas não é. É o mesmo que

 [myView frame].size.width = newWidth; 

o que não funciona. O compilador aceita o primeiro, mas não o segundo. E mesmo que tenha emitido um erro ou aviso para o primeiro, isso é apenas confuso.

Me chame de preguiçoso, mas se eu tivesse que digitar um ‘. vs dois [] cada vez para obter os mesmos resultados eu preferiria um single. Eu odeio idiomas verbosos. O () em lisp me deixou louco. Linguagens elegantes como a matemática são concisas e eficazes, todas as outras são insuficientes.

Use a notação de pontos (sempre que puder)

Em methods de instância retornando algum valor

Não use notação de ponto

Nos methods de instância, retornando void, nos methods init ou no método de class.

E minha exceção favorita pessoal

 NSMutableArray * array = @[].mutableCopy; 

Eu pessoalmente não uso notação de ponto no código. Eu só uso na expressão de binding CoreData KVC quando necessário.

A razão para não usá-los no código para mim é que a notação de ponto esconde a semântica do setter. Definir uma propriedade em notação de ponto sempre se parece com a atribuição, independentemente da semântica do setter (atribuir / reter / copiar). Usar a notação de mensagem torna visível que o object receptor tem controle sobre o que acontece no setter e sublinha o fato de que esses efeitos precisam ser considerados.

Eu ainda estou considerando se eu poderia querer usar notação de ponto ao recuperar o valor de uma propriedade declarada ou compatível com KVC porque ela é reconhecidamente um pouco mais compacta e legível e não há semântica oculta. No momento, estou mantendo a notação de mensagens para manter a consistência.

OK, a notação de ponto no Objective-C parece estranha, de fato. Mas ainda não consigo fazer o seguinte sem ele:

 int width = self.frame.size.width; 

Funciona bem, mas:

 int height = [[[self frame] size] height]; 

Dá-me “Não é possível converter para um tipo de ponteiro”. Eu realmente gostaria de manter meu código consistente com a notação de mensagens, no entanto.

Esta é uma ótima pergunta e vejo muitas respostas diferentes para isso. Embora muitos tenham abordado os tópicos, tentarei responder a isso de um ângulo diferente (alguns podem ter feito isso implicitamente):

Se usarmos a notação ‘ponto’, a resolução do alvo para o método é feita em tempo de compilation. Se usarmos a passagem de mensagens, a resolução do destino será adiada para a execução em tempo de execução. Se os destinos forem resolvidos em tempo de compilation, a execução será mais rápida, pois a resolução dos destinos no tempo de execução inclui algumas sobrecargas. (Não que a diferença de tempo importará muito). Como já definimos a propriedade na interface do object, não faz sentido diferir a resolução do destino de uma propriedade para o tempo de execução e, portanto, a notação de ponto é a notação que devemos usar para acessar a propriedade.