Objective-C não possui namespaces; é muito parecido com C, tudo está dentro de um espaço para nome global. A prática comum é prefixar as classes com as iniciais; por exemplo, se você estiver trabalhando na IBM, poderá prefixá-las com "IBM"; se você trabalha na Microsoft, pode usar "MS"; e assim por diante. Às vezes, as iniciais se referem ao projeto, por exemplo, o Adium prefixa as classes com "AI" (como não há nenhuma empresa por trás disso, você pode pegar as iniciais). A Apple prefixa as classes com o NS e diz que esse prefixo é reservado apenas para a Apple.
Até aqui tudo bem. Mas anexar 2 a 4 letras a um nome de classe na frente é um espaço para nome muito, muito limitado. Por exemplo, MS ou AI podem ter significados completamente diferentes (AI pode ser Inteligência Artificial, por exemplo) e algum outro desenvolvedor pode decidir usá-los e criar uma classe com o mesmo nome. Bang , colisão de namespace.
Ok, se essa é uma colisão entre uma de suas próprias classes e uma de uma estrutura externa que você está usando, você pode facilmente mudar o nome da sua classe, não é grande coisa. Mas e se você usar duas estruturas externas, ambas para as quais você não tem a fonte e para as quais não pode mudar? Seu aplicativo é vinculado a ambos e você obtém conflitos de nome. Como você resolveria isso? Qual é a melhor maneira de contorná-los de tal maneira que você ainda possa usar as duas classes?
Em C, você pode solucionar esses problemas não vinculando diretamente à biblioteca; em vez disso, carrega a biblioteca em tempo de execução, usando dlopen (), encontre o símbolo que procura usando dlsym () e atribua-o a um símbolo global (que você pode nomear como quiser) e acessá-lo através deste símbolo global. Por exemplo, se você tiver um conflito porque alguma biblioteca C possui uma função chamada open (), você pode definir uma variável chamada myOpen e apontar para a função open () da biblioteca, portanto, quando desejar usar o sistema open () , você apenas usa open () e quando deseja usar o outro, acessa-o através do identificador myOpen.
Existe algo semelhante possível no Objective-C e, caso contrário, existe alguma outra solução inteligente e complicada que você possa usar para resolver conflitos de namespace? Alguma ideia?
Atualizar:
Apenas para esclarecer isso: respostas que sugerem como evitar colisões com espaço para nome antecipadamente ou como criar um espaço para nome melhor são certamente bem-vindas; no entanto, não os aceitarei como resposta, pois eles não resolvem o meu problema. Eu tenho duas bibliotecas e seus nomes de classe colidem. Eu não posso mudá-los; Eu não tenho a fonte de nenhum dos dois. A colisão já está lá e dicas sobre como poderia ter sido evitada com antecedência não ajudarão mais. Posso encaminhá-los aos desenvolvedores dessas estruturas e espero que eles escolham um espaço para nome melhor no futuro, mas, por enquanto, estou procurando uma solução para trabalhar com as estruturas agora mesmo em um único aplicativo. Alguma solução para tornar isso possível?
fonte
Respostas:
Se você não precisa usar classes de ambas as estruturas ao mesmo tempo e está direcionando plataformas que suportam o descarregamento do NSBundle (OS X 10.4 ou posterior, sem suporte ao GNUStep), e o desempenho realmente não é um problema para você, acredito que você pode carregar uma estrutura toda vez que precisar usar uma classe e, em seguida, descarregá-la e carregar a outra quando precisar usar a outra estrutura.
Minha idéia inicial era usar o NSBundle para carregar uma das estruturas, copiar ou renomear as classes dentro dessa estrutura e, em seguida, carregar a outra estrutura. Existem dois problemas com isso. Primeiro, não consegui encontrar uma função para copiar os dados apontados para renomear ou copiar uma classe, e qualquer outra classe nessa primeira estrutura que faça referência à classe renomeada agora faria referência à classe da outra estrutura.
Você não precisaria copiar ou renomear uma classe se houvesse uma maneira de copiar os dados apontados por um IMP. Você pode criar uma nova classe e depois copiar sobre ivars, métodos, propriedades e categorias. Muito mais trabalho, mas é possível. No entanto, você ainda teria um problema com as outras classes na estrutura que referenciam a classe errada.
EDIT: A diferença fundamental entre os tempos de execução C e Objective-C é, pelo que entendi, quando as bibliotecas são carregadas, as funções nessas bibliotecas contêm ponteiros para qualquer símbolo a que se referem, enquanto que no Objective-C, elas contêm representações de string do nomes desses símbolos. Assim, no seu exemplo, você pode usar o dlsym para obter o endereço do símbolo na memória e anexá-lo a outro símbolo. O outro código da biblioteca ainda funciona porque você não está alterando o endereço do símbolo original. O Objective-C usa uma tabela de pesquisa para mapear nomes de classes para endereços, e é um mapeamento 1-1, portanto você não pode ter duas classes com o mesmo nome. Portanto, para carregar as duas classes, uma delas deve ter seu nome alterado. No entanto, quando outras classes precisam acessar uma das classes com esse nome,
fonte
Prefixar suas classes com um prefixo exclusivo é fundamentalmente a única opção, mas existem várias maneiras de tornar isso menos oneroso e feio. Há uma longa discussão de opções aqui . O meu favorito é a
@compatibility_alias
diretiva de compilador Objective-C (descrita aqui ). Você pode usar@compatibility_alias
para "renomear" uma classe, permitindo que você nomeie sua classe usando o FQDN ou algum prefixo:Como parte de uma estratégia completa, você pode prefixar todas as suas classes com um prefixo exclusivo, como o FQDN, e criar um cabeçalho com todos os
@compatibility_alias
(imagino que você possa gerar automaticamente o referido cabeçalho).A desvantagem de prefixar assim é que você deve inserir o nome verdadeiro da classe (por exemplo,
COM_WHATEVER_ClassName
acima) em qualquer coisa que precise do nome da classe de uma string além do compilador. Notavelmente,@compatibility_alias
é uma diretiva de compilador, não uma função de tempo de execução, portantoNSClassFromString(ClassName)
falhará (retornaránil
) - você terá que usarNSClassFromString(COM_WHATERVER_ClassName)
. É possível usar aibtool
fase de construção para modificar os nomes de classe em um nib / xib do Interface Builder, para que você não precise gravar o COM_WHATEVER _... completo no Interface Builder.Advertência final: como essa é uma diretiva de compilador (e obscura), ela pode não ser portátil entre os compiladores. Em particular, não sei se funciona com o front-end Clang do projeto LLVM, embora deva funcionar com o LLVM-GCC (LLVM usando o front-end do GCC).
fonte
Várias pessoas já compartilharam algum código complicado e inteligente que pode ajudar a resolver o problema. Algumas sugestões podem funcionar, mas todas são inferiores ao ideal e algumas são absolutamente desagradáveis de implementar. (Às vezes, hacks feios são inevitáveis, mas tento evitá-los sempre que posso.) Do ponto de vista prático, aqui estão minhas sugestões.
Suponho que taxas, termos e durações de licenciamento possam impedir ações instantâneas em qualquer um desses pontos. Espero que você consiga resolver o conflito o mais rápido possível. Boa sorte!
fonte
Isso é grosseiro, mas você pode usar objetos distribuídos para manter uma das classes apenas no endereço de um programa subordinado e no RPC. Isso ficará confuso se você estiver passando uma tonelada de coisas para frente e para trás (e pode não ser possível se as duas classes estiverem manipulando diretamente visualizações, etc.).
Existem outras soluções em potencial, mas muitas delas dependem da situação exata. Em particular, você está usando os tempos de execução modernos ou herdados, sua arquitetura única ou de 32 ou 64 bits, quais versões do sistema operacional você está almejando, vinculando dinamicamente, vinculando estaticamente ou tem uma opção, e é potencialmente Não há problema em fazer algo que possa exigir manutenção para novas atualizações de software.
Se você está realmente desesperado, o que você pode fazer é:
O exposto acima será bastante trabalhoso, e se você precisar implementá-lo em vários arcos e em diferentes versões de tempo de execução, será muito desagradável, mas definitivamente pode ser feito para funcionar.
fonte
Você já pensou em usar as funções de tempo de execução (/usr/include/objc/runtime.h) para clonar uma das classes conflitantes em uma classe sem colisão e depois carregar a estrutura de classe em colisão? (isso exigiria que as estruturas em colisão fossem carregadas em momentos diferentes para funcionar.)
É possível inspecionar as classes ivars, métodos (com nomes e endereços de implementação) e nomes com o tempo de execução e criar seus próprios dinamicamente para ter o mesmo layout ivar, nomes de métodos / endereços de implementação e diferir apenas por nome (para evitar o colisão)
fonte
Situações desesperadas exigem medidas desesperadas. Você já pensou em invadir o código do objeto (ou arquivo de biblioteca) de uma das bibliotecas, alterando o símbolo de colisão para um nome alternativo - do mesmo tamanho, mas com uma ortografia diferente (mas, recomendação, o mesmo tamanho do nome)? Inerentemente desagradável.
Não está claro se o seu código está chamando diretamente as duas funções com o mesmo nome, mas com implementações diferentes ou se o conflito é indireto (nem está claro se isso faz alguma diferença). No entanto, há pelo menos uma chance externa de que a renomeação funcione. Também pode ser uma idéia minimizar a diferença nas grafias, para que, se os símbolos estiverem em uma ordem classificada em uma tabela, a renomeação não altere as coisas. Coisas como a pesquisa binária ficam chateadas se a matriz que eles estão pesquisando não está na ordem de classificação conforme o esperado.
fonte
@compatibility_alias
será capaz de resolver conflitos de namespace de classe, por exemploNo entanto, isso não resolverá nenhuma das enumerações, typedefs ou colisões de namespace de protocolo . Além disso, ele não funciona bem com
@class
decls avançados da classe original. Como a maioria das estruturas virá com coisas que não são de classe, como typedefs, você provavelmente não conseguirá corrigir o problema de espaço para nome apenas com a compatibilidade_alias.Eu olhei para um problema semelhante ao seu , mas tinha acesso à fonte e estava construindo as estruturas. A melhor solução que encontrei para isso foi usar
@compatibility_alias
condicionalmente com #defines para suportar enumerações / typedefs / protocolos / etc. Você pode fazer isso condicionalmente na unidade de compilação do cabeçalho em questão para minimizar o risco de expandir coisas na outra estrutura em colisão.fonte
Parece que o problema é que você não pode fazer referência a arquivos de cabeçalhos de ambos os sistemas na mesma unidade de tradução (arquivo de origem). Se você criar wrappers de objetivo-c em torno das bibliotecas (tornando-os mais utilizáveis no processo) e #incluir apenas os cabeçalhos de cada biblioteca na implementação das classes de wrapper, isso separaria efetivamente as colisões de nomes.
Eu não tenho experiência suficiente com isso no objetivo-c (apenas começando), mas acredito que é isso que eu faria em C.
fonte
Prefixar os arquivos é a solução mais simples que eu conheço. Cocoadev tem uma página de espaço para nome, que é um esforço da comunidade para evitar colisões de espaço para nome. Sinta-se livre para adicionar seu próprio a esta lista, acredito que é para isso.
http://www.cocoadev.com/index.pl?ChooseYourOwnPrefix
fonte
Se você tiver uma colisão, sugiro que pense bem em como refatorar uma das estruturas do seu aplicativo. Ter uma colisão sugere que os dois estão fazendo coisas semelhantes e é provável que você possa usar uma estrutura extra simplesmente refatorando seu aplicativo. Isso não apenas resolveria o problema do espaço para nome, como também tornaria o seu código mais robusto, mais fácil de manter e mais eficiente.
Sobre uma solução mais técnica, se eu estivesse na sua posição, essa seria minha escolha.
fonte
Se a colisão ocorrer apenas no nível do link estático, você poderá escolher qual biblioteca é usada para resolver os símbolos:
Se
foo.o
ebar.o
ambos referência o símbolorat
em seguida,libdog
irá resolverfoo.o
'srat
elibcat
vai resolverbar.o
érat
.fonte
Apenas um pensamento .. não testado ou comprovado e pode ser um marco, mas você já pensou em escrever um adaptador para a classe que você usa a partir das estruturas mais simples .. ou pelo menos suas interfaces?
Se você escrevesse um invólucro em torno das estruturas mais simples (ou aquelas com menos acesso às interfaces), não seria possível compilar esse invólucro em uma biblioteca. Dado que a biblioteca é pré-compilada e apenas seus cabeçalhos precisam ser distribuídos, você estaria efetivamente ocultando a estrutura subjacente e estaria livre para combiná-la com a segunda estrutura com conflito.
Compreendo, é claro, que provavelmente haverá momentos em que você precisará usar as classes de ambas as estruturas ao mesmo tempo; no entanto, você poderá fornecer fábricas para outros adaptadores de classe dessa estrutura. Na parte de trás desse ponto, acho que você precisaria de um pouco de refatoração para extrair as interfaces que você está usando das duas estruturas, o que deve fornecer um bom ponto de partida para você construir seu wrapper.
Você pode desenvolver a biblioteca como você e quando precisar de mais funcionalidades da biblioteca agrupada e simplesmente recompilar quando alterar.
Novamente, de maneira alguma comprovada, mas senti vontade de acrescentar uma perspectiva. espero que ajude :)
fonte
Se você tiver duas estruturas com o mesmo nome de função, tente carregar dinamicamente as estruturas. Será deselegante, mas possível. Como fazer isso com as classes Objective-C, eu não sei. Suponho que a
NSBundle
classe terá métodos que carregarão uma classe específica.fonte