Como um desenvolvedor Java que está lendo a documentação Objective-C 2.0 da Apple: Eu me pergunto o que " enviar uma mensagem para o zero " significa - sem falar em como é realmente útil. Pegando um trecho da documentação:
Existem vários padrões no Cocoa que se aproveitam desse fato. O valor retornado de uma mensagem para nulo também pode ser válido:
- Se o método retornar um objeto, qualquer tipo de ponteiro, qualquer escalar inteiro de tamanho menor ou igual a sizeof (void *), um flutuante, um duplo, um duplo longo ou um longo longo, então uma mensagem enviada para nil retornará 0 .
- Se o método retornar uma estrutura, conforme definido pelo Guia de chamada de função ABI do Mac OS X para ser retornada em registradores, uma mensagem enviada para nil retornará 0,0 para cada campo na estrutura de dados. Outros tipos de dados de estrutura não serão preenchidos com zeros.
- Se o método retornar qualquer coisa diferente dos tipos de valor mencionados, o valor de retorno de uma mensagem enviada para nil é indefinido.
Java tornou meu cérebro incapaz de grocar a explicação acima? Ou há algo que estou perdendo que tornaria isso tão claro quanto o vidro?
Eu tenho a idéia de mensagens / receptores em Objective-C, estou simplesmente confuso sobre um receptor que por acaso é nil
.
objective-c
Ryan Delucchi
fonte
fonte
Respostas:
Bem, acho que pode ser descrito usando um exemplo muito artificial. Digamos que você tenha um método em Java que imprime todos os elementos em um ArrayList:
Agora, se você chamar esse método assim: someObject.foo (NULL); você provavelmente obterá um NullPointerException quando tentar acessar a lista, neste caso, na chamada para list.size (); Agora, você provavelmente nunca chamaria someObject.foo (NULL) com o valor NULL assim. No entanto, você pode ter obtido sua ArrayList de um método que retorna NULL se ocorrer algum erro ao gerar a ArrayList como someObject.foo (otherObject.getArrayList ());
Claro, você também terá problemas se fizer algo assim:
Agora, em Objective-C, temos o método equivalente:
Agora, se tivermos o seguinte código:
temos a mesma situação em que Java produzirá um NullPointerException. O objeto nil será acessado primeiro em [anArray count] No entanto, em vez de lançar um NullPointerException, Objective-C simplesmente retornará 0 de acordo com as regras acima, de modo que o loop não será executado. No entanto, se definirmos o loop para executar um determinado número de vezes, primeiro enviaremos uma mensagem para anArray em [anArray objectAtIndex: i]; Isso também retornará 0, mas como objectAtIndex: retorna um ponteiro, e um ponteiro para 0 é nulo / NULL, NSLog será passado nulo a cada vez que passar pelo loop. (Embora NSLog seja uma função e não um método, ele imprime (nulo) se for passado um NSString nulo.
Em alguns casos, é melhor ter um NullPointerException, pois você pode dizer imediatamente que algo está errado com o programa, mas a menos que você pegue a exceção, o programa irá travar. (Em C, tentar cancelar a referência de NULL dessa forma faz com que o programa trave.) Em Objective-C, isso apenas causa um comportamento possivelmente incorreto em tempo de execução. No entanto, se você tiver um método que não quebra se retornar 0 / nil / NULL / uma estrutura zerada, isso evita que você tenha que verificar se o objeto ou os parâmetros são nulos.
fonte
myObject->iVar
isso irá travar, independente se for C com ou sem objetos. (desculpe gravedig.)->
não é mais uma operação Objective-C, mas sim um C-ismo genérico.À mensagem
nil
não faz nada e retornanil
,Nil
,NULL
,0
, ou0.0
.fonte
Todas as outras postagens estão corretas, mas talvez seja o conceito que é importante aqui.
Em chamadas de método Objective-C, qualquer referência de objeto que pode aceitar um seletor é um destino válido para esse seletor.
Isso economiza MUITO "o objeto de destino é do tipo X?" código - contanto que o objeto receptor implemente o seletor, não faz absolutamente nenhuma diferença de que classe ele é!
nil
é um NSObject que aceita qualquer seletor - ele simplesmente não faz nada. Isso elimina muito do código "verificar se há nulo, não envie a mensagem se for verdadeiro". (O conceito "se ele aceita, ele implementa" também é o que permite criar protocolos , que são quase como interfaces Java: uma declaração de que se uma classe implementa os métodos declarados, ela está em conformidade com o protocolo.)A razão para isso é eliminar o código monkey que não faz nada, exceto manter o compilador feliz. Sim, você obtém a sobrecarga de mais uma chamada de método, mas economiza tempo do programador , que é um recurso muito mais caro do que o tempo da CPU. Além disso, você está eliminando mais código e mais complexidade condicional de seu aplicativo.
Esclarecendo para os downvoters: você pode pensar que este não é um bom caminho a percorrer, mas é como a linguagem é implementada e é o idioma de programação recomendado em Objective-C (consulte as aulas de programação do iPhone em Stanford).
fonte
O que significa é que o tempo de execução não produz um erro quando objc_msgSend é chamado no ponteiro nil; em vez disso, retorna algum valor (geralmente útil). As mensagens que podem ter um efeito colateral não fazem nada.
É útil porque a maioria dos valores padrão é mais apropriada do que um erro. Por exemplo:
Ou seja, nil parece ser a matriz vazia. Ocultar uma referência NSView nula não faz nada. Handy, hein?
fonte
Na citação da documentação, existem dois conceitos separados - talvez seja melhor se a documentação deixar isso mais claro:
O primeiro é provavelmente mais relevante aqui: normalmente, ser capaz de enviar mensagens para
nil
torna o código mais direto - você não precisa verificar se há valores nulos em todos os lugares. O exemplo canônico é provavelmente o método acessador:Se o envio de mensagens para
nil
não fosse válido, esse método seria mais complexo - você teria que ter duas verificações adicionais para garantirvalue
enewValue
nãonil
antes de enviar mensagens.O último ponto (os valores retornados de uma mensagem
nil
também são normalmente válidos), entretanto, adiciona um efeito multiplicador ao primeiro. Por exemplo:Este código novamente não requer uma verificação de
nil
valores e flui naturalmente ...Dito isso, a flexibilidade adicional de poder enviar mensagens
nil
tem algum custo. Existe a possibilidade de, em algum estágio, você escrever um código que falhe de maneira peculiar porque você não levou em consideração a possibilidade de que um valor possa sernil
.fonte
De Greg Parker 's local :
Se estiver executando o LLVM Compiler 3.0 (Xcode 4.2) ou posterior
fonte
Isso significa muitas vezes não ter que verificar se há objetos nulos em todos os lugares para segurança - particularmente:
ou, como observado, vários métodos de contagem e comprimento retornam 0 quando você tem um valor nulo, portanto, não é necessário adicionar verificações extras para nil por completo:
ou isto:
fonte
Não pense em "o receptor ser nulo"; Eu concordo, isso é muito estranho. Se você está enviando uma mensagem para nil, não há receptor. Você está apenas enviando uma mensagem para o nada.
Como lidar com isso é uma diferença filosófica entre Java e Objective-C: em Java, isso é um erro; em Objective-C, é um ambiente autônomo.
fonte
Mensagens ObjC que são enviadas para nil e cujos valores de retorno têm tamanho maior que sizeof (void *) produzem valores indefinidos em processadores PowerPC. Além disso, essas mensagens fazem com que valores indefinidos sejam retornados em campos de estruturas cujo tamanho é maior que 8 bytes nos processadores Intel também. Vincent Gable descreveu isso muito bem em sua postagem do blog
fonte
Eu não acho que nenhuma das outras respostas mencionou isso claramente: se você está acostumado com Java, deve ter em mente que, embora Objective-C no Mac OS X tenha suporte para manipulação de exceção, é um recurso de linguagem opcional que pode ser ligado / desligado com um sinalizador do compilador. Meu palpite é que esse design de "enviar mensagens para
nil
é seguro" antecede a inclusão do suporte ao tratamento de exceções na linguagem e foi feito com um objetivo semelhante em mente: os métodos podem retornarnil
para indicar erros, e como o envio de uma mensagem paranil
geralmente retornanil
por sua vez, isso permite que a indicação de erro se propague em seu código, para que você não precise verificá-la em cada mensagem. Você só precisa verificar se há pontos importantes. Pessoalmente, acho que a propagação e o tratamento de exceções são a melhor maneira de abordar esse objetivo, mas nem todos concordam com isso. (Por outro lado, eu, por exemplo, não gosto da exigência de Java de você ter que declarar quais exceções um método pode lançar, o que muitas vezes força você a propagar sintaticamente declarações de exceção em todo o seu código; mas isso é outra discussão.)Eu postei uma resposta semelhante, mas mais longa, para a pergunta relacionada "Afirmar que toda criação de objeto foi necessária com sucesso no Objetivo C?" se você quiser mais detalhes.
fonte
nil
retorno frequentemente usado para curto-circuitar uma cadeia em um NO-op.C não representa nada como 0 para valores primitivos e NULL para ponteiros (que é equivalente a 0 em um contexto de ponteiro).
Objective-C se baseia na representação de C de nada, adicionando nil. nil é um ponteiro de objeto para nada. Embora semanticamente distintos de NULL, eles são tecnicamente equivalentes um ao outro.
NSObjects recém-alocados começam a vida com seu conteúdo definido como 0. Isso significa que todos os ponteiros que o objeto tem para outros objetos começam como nil, portanto, é desnecessário, por exemplo, definir self. (Associação) = nil nos métodos init.
O comportamento mais notável de nil, entretanto, é que ele pode receber mensagens.
Em outras linguagens, como C ++ (ou Java), isso travaria seu programa, mas em Objective-C, invocar um método em nil retorna um valor zero. Isso simplifica muito as expressões, pois elimina a necessidade de verificar se há nulo antes de fazer qualquer coisa:
Estar ciente de como o nil funciona em Objective-C permite que essa conveniência seja um recurso, e não um bug oculto em seu aplicativo. Certifique-se de se proteger contra casos em que os valores nulos são indesejados, verificando e retornando antecipadamente para falhar silenciosamente ou adicionando um NSParameterAssert para lançar uma exceção.
Fonte: http://nshipster.com/nil/ https://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocObjectsClasses.html (Enviando mensagem para nil).
fonte