Objective-C no tiene espacios de nombres; es muy parecido a C, todo está dentro de un espacio de nombres global. La práctica común es prefijar las clases con iniciales, por ejemplo, si está trabajando en IBM, podría prefijarlas con "IBM"; si trabaja para Microsoft, puede usar "MS"; y así. A veces las iniciales se refieren al proyecto, por ejemplo, las clases de prefijos de Adium con "AI" (ya que no hay ninguna compañía detrás de eso, podría tomar las iniciales). Apple clasifica los prefijos con NS y dice que este prefijo está reservado solo para Apple.
Hasta ahora todo bien. Pero agregar de 2 a 4 letras al nombre de una clase en frente es un espacio de nombres muy, muy limitado. Por ejemplo, MS o AI podrían tener significados completamente diferentes (AI podría ser Inteligencia Artificial, por ejemplo) y algún otro desarrollador podría decidir usarlos y crear una clase con el mismo nombre. Bang , colisión de espacio de nombres.
De acuerdo, si se trata de una colisión entre una de sus propias clases y una de un marco externo que está utilizando, puede cambiar fácilmente el nombre de su clase, no es gran cosa. Pero, ¿qué sucede si usa dos marcos externos, ambos marcos para los que no tiene la fuente y que no puede cambiar? Su aplicación se vincula con ambos y obtiene conflictos de nombres. ¿Cómo harías para resolver esto? ¿Cuál es la mejor manera de evitarlos de tal manera que aún pueda usar ambas clases?
En C puede solucionar estos problemas al no vincular directamente a la biblioteca, en su lugar, cargar la biblioteca en tiempo de ejecución, usar dlopen (), luego encontrar el símbolo que está buscando usando dlsym () y asignarlo a un símbolo global (que usted puede nombrar de la forma que desee) y luego acceder a él a través de este símbolo global. Por ejemplo, si tiene un conflicto porque alguna biblioteca C tiene una función llamada open (), puede definir una variable llamada myOpen y hacer que apunte a la función open () de la biblioteca, por lo tanto, cuando desee usar el sistema open () , solo usa open () y cuando desea usar el otro, accede a él a través del identificador myOpen.
¿Es posible algo similar en Objective-C? Si no, ¿hay alguna otra solución inteligente y difícil que pueda usar para resolver conflictos de espacio de nombres? ¿Algunas ideas?
Actualizar:
Solo para aclarar esto: las respuestas que sugieren cómo evitar las colisiones del espacio de nombres por adelantado o cómo crear un mejor espacio de nombres son ciertamente bienvenidas; sin embargo, no los aceptaré como respuesta ya que no resuelven mi problema. Tengo dos bibliotecas y sus nombres de clase chocan. No puedo cambiarlos. No tengo la fuente de ninguno de los dos. La colisión ya está allí y los consejos sobre cómo podría haberse evitado de antemano ya no ayudarán. Puedo reenviarlos a los desarrolladores de estos marcos y espero que elijan un mejor espacio de nombres en el futuro, pero por el momento estoy buscando una solución para trabajar con los marcos en este momento dentro de una sola aplicación. ¿Alguna solución para hacer esto posible?
fuente
Respuestas:
Si no necesita usar clases de ambos marcos al mismo tiempo, y está apuntando a plataformas que admiten la descarga de NSBundle (OS X 10.4 o posterior, no es compatible con GNUStep), y el rendimiento realmente no es un problema para usted, creo que puede cargar un marco cada vez que necesite usar una clase de él, y luego descargarlo y cargar el otro cuando necesite usar el otro marco.
Mi idea inicial era usar NSBundle para cargar uno de los marcos, luego copiar o cambiar el nombre de las clases dentro de ese marco y luego cargar el otro marco. Hay dos problemas con esto. Primero, no pude encontrar una función para copiar los datos que apuntaban a renombrar o copiar una clase, y cualquier otra clase en ese primer marco que haga referencia a la clase renombrada ahora haría referencia a la clase desde el otro marco.
No necesitaría copiar o cambiar el nombre de una clase si hubiera una forma de copiar los datos señalados por un IMP. Puede crear una nueva clase y luego copiar sobre ivars, métodos, propiedades y categorías. Mucho más trabajo, pero es posible. Sin embargo, aún tendría un problema con las otras clases en el marco que hacen referencia a la clase incorrecta.
EDITAR: La diferencia fundamental entre los tiempos de ejecución C y Objective-C es, según tengo entendido, cuando las bibliotecas se cargan, las funciones en esas bibliotecas contienen punteros a los símbolos a los que hacen referencia, mientras que en Objective-C, contienen representaciones de cadena de nombres de estos símbolos. Por lo tanto, en su ejemplo, puede usar dlsym para obtener la dirección del símbolo en la memoria y adjuntarlo a otro símbolo. El otro código en la biblioteca aún funciona porque no está cambiando la dirección del símbolo original. Objective-C utiliza una tabla de búsqueda para asignar nombres de clases a direcciones, y es una asignación 1-1, por lo que no puede tener dos clases con el mismo nombre. Por lo tanto, para cargar ambas clases, una de ellas debe tener su nombre cambiado. Sin embargo, cuando otras clases necesitan acceder a una de las clases con ese nombre,
fuente
Prefijar sus clases con un prefijo único es fundamentalmente la única opción, pero hay varias formas de hacerlo menos oneroso y feo. Hay una larga discusión de opciones aquí . Mi favorita es la
@compatibility_alias
directiva del compilador Objective-C (descrita aquí ). Puede usar@compatibility_alias
para "cambiar el nombre" de una clase, lo que le permite nombrar su clase utilizando FQDN o algún prefijo:Como parte de una estrategia completa, puede prefijar todas sus clases con un prefijo único como el FQDN y luego crear un encabezado con todos los
@compatibility_alias
(imagino que podría generar automáticamente dicho encabezado).La desventaja de los prefijos como este es que debe ingresar el nombre de clase verdadero (por ejemplo,
COM_WHATEVER_ClassName
arriba) en cualquier cosa que necesite el nombre de clase de una cadena además del compilador. En particular,@compatibility_alias
es una directiva del compilador, no una función de tiempo de ejecución, porNSClassFromString(ClassName)
lo que fallará (volveránil
): tendrá que usarlaNSClassFromString(COM_WHATERVER_ClassName)
. Puedes usaribtool
través de la fase de compilación para modificar los nombres de clase en un nib / xib de Interface Builder para que no tenga que escribir COM_WHATEVER _... completo en Interface Builder.Advertencia final: debido a que esta es una directiva del compilador (y oscura), puede que no sea portátil entre los compiladores. En particular, no sé si funciona con la interfaz Clang del proyecto LLVM, aunque debería funcionar con LLVM-GCC (LLVM utilizando la interfaz GCC).
fuente
Varias personas ya han compartido un código complicado e inteligente que podría ayudar a resolver el problema. Algunas de las sugerencias pueden funcionar, pero todas son menos que ideales, y algunas son francamente desagradables de implementar. (A veces, los trucos feos son inevitables, pero trato de evitarlos siempre que puedo). Desde un punto de vista práctico, aquí están mis sugerencias.
Supongo que las tarifas de licencia, los términos y las duraciones pueden evitar la acción instantánea en cualquiera de estos puntos. Esperemos que pueda resolver el conflicto lo antes posible. ¡Buena suerte!
fuente
Esto es asqueroso, pero podrías usar objetos distribuidos para mantener una de las clases solo en una dirección de programas subordinados y RPC. Eso se volverá complicado si está pasando un montón de cosas de un lado a otro (y puede que no sea posible si ambas clases manipulan vistas directamente, etc.).
Existen otras posibles soluciones, pero muchas de ellas dependen de la situación exacta. En particular, ¿está utilizando los tiempos de ejecución modernos o heredados, tiene una arquitectura única o gruesa, de 32 o 64 bits, a qué versiones del sistema operativo está apuntando, está vinculando dinámicamente, vinculando estáticamente, o tiene una opción, y es potencialmente está bien hacer algo que pueda requerir mantenimiento para nuevas actualizaciones de software.
Si está realmente desesperado, lo que podría hacer es:
Lo anterior va a ser bastante laborioso, y si necesita implementarlo contra múltiples arcos y diferentes versiones de tiempo de ejecución, será muy desagradable, pero definitivamente se puede hacer que funcione.
fuente
¿Ha considerado usar las funciones de tiempo de ejecución (/usr/include/objc/runtime.h) para clonar una de las clases en conflicto en una clase que no colisiona y luego cargar el marco de clase de colisión? (esto requeriría que los marcos de colisión se carguen en diferentes momentos para funcionar).
Puede inspeccionar las clases ivars, métodos (con nombres y direcciones de implementación) y nombres con el tiempo de ejecución, y crear los suyos también dinámicamente para tener el mismo diseño ivar, nombres de métodos / direcciones de implementación, y solo diferir por nombre (para evitar el colisión)
fuente
Situaciones desesperadas requieren medidas desesperadas. ¿Ha considerado hackear el código objeto (o archivo de biblioteca) de una de las bibliotecas, cambiando el símbolo de colisión a un nombre alternativo, de la misma longitud pero una ortografía diferente (pero, recomendación, la misma longitud de nombre)? Inherentemente desagradable.
No está claro si su código está llamando directamente a las dos funciones con el mismo nombre pero implementaciones diferentes o si el conflicto es indirecto (ni está claro si hace alguna diferencia). Sin embargo, hay al menos una posibilidad externa de que el cambio de nombre funcione. También podría ser una idea minimizar la diferencia en la ortografía, de modo que si los símbolos están ordenados en una tabla, el cambio de nombre no cambia las cosas. Cosas como la búsqueda binaria se alteran si la matriz que están buscando no está ordenada como se esperaba.
fuente
@compatibility_alias
podrá resolver conflictos de espacio de nombres de clase, por ejemploSin embargo, esto no resolverá ninguna de las enumeraciones, definiciones de tipo o colisiones de espacio de nombres de protocolo . Además, no juega bien con
@class
decls hacia adelante de la clase original. Dado que la mayoría de los frameworks vendrán con estas cosas que no son de clase, como typedefs, es probable que no pueda solucionar el problema de espacio de nombres solo con compatibilidad_alias.Miré un problema similar al tuyo , pero tenía acceso a la fuente y estaba construyendo los marcos. La mejor solución que encontré para esto fue usar
@compatibility_alias
condicionalmente con #defines para admitir las enumeraciones / typedefs / protocolos / etc. Puede hacer esto condicionalmente en la unidad de compilación del encabezado en cuestión para minimizar el riesgo de expandir cosas en el otro marco de colisión.fuente
Parece que el problema es que no puede hacer referencia a archivos de encabezados de ambos sistemas en la misma unidad de traducción (archivo fuente). Si crea contenedores de Object-C alrededor de las bibliotecas (haciéndolos más utilizables en el proceso), y solo #incluye los encabezados para cada biblioteca en la implementación de las clases de contenedor, eso efectivamente separaría las colisiones de nombres.
No tengo suficiente experiencia con esto en el objetivo-c (recién comenzando), pero creo que eso es lo que haría en C.
fuente
Prefijar los archivos es la solución más simple que conozco. Cocoadev tiene una página de espacio de nombres que es un esfuerzo de la comunidad para evitar colisiones de espacios de nombres. Siéntase libre de agregar el suyo propio a esta lista, creo que para eso es.
http://www.cocoadev.com/index.pl?ChooseYourOwnPrefix
fuente
Si tiene una colisión, le sugiero que piense detenidamente cómo podría refactorizar uno de los marcos de su aplicación. Tener una colisión sugiere que los dos están haciendo cosas similares a las suyas, y es probable que pueda moverse usando un marco adicional simplemente refactorizando su aplicación. Esto no solo resolvería su problema de espacio de nombres, sino que haría que su código sea más robusto, más fácil de mantener y más eficiente.
Sobre una solución más técnica, si estuviera en su posición, esta sería mi elección.
fuente
Si la colisión es solo en el nivel de enlace estático, puede elegir qué biblioteca se utiliza para resolver símbolos:
Si
foo.o
ybar.o
tanto el símbolo de referenciarat
a continuación,libdog
se resolveráfoo.o
'srat
ylibcat
resolverábar.o
' srat
.fuente
Solo un pensamiento ... no probado o comprobado y podría ser la marca, pero ¿ha considerado escribir un adaptador para la clase que utiliza desde el marco más simple ... o al menos sus interfaces?
Si tuviera que escribir un contenedor alrededor del marco más simple (o de las interfaces a las que accede menos), no sería posible compilar ese contenedor en una biblioteca. Dado que la biblioteca está precompilada y solo su necesario distribuir encabezados, estaría ocultando efectivamente el marco subyacente y sería libre de combinarlo con el segundo marco con el choque.
Aprecio, por supuesto, que es probable que haya ocasiones en las que necesite usar las clases de ambos marcos al mismo tiempo, sin embargo, podría proporcionar fábricas para otros adaptadores de clase de ese marco. En la parte posterior de ese punto, supongo que necesitaría un poco de refactorización para extraer las interfaces que está utilizando de ambos marcos, lo que debería proporcionar un buen punto de partida para construir su contenedor.
Puede construir sobre la biblioteca como usted y cuando necesite más funcionalidad de la biblioteca envuelta, y simplemente recompilar cuando cambie.
Una vez más, de ninguna manera probado, pero tenía ganas de agregar una perspectiva. Espero eso ayude :)
fuente
Si tiene dos marcos que tienen el mismo nombre de función, puede intentar cargar dinámicamente los marcos. Será poco elegante, pero posible. Cómo hacerlo con las clases de Objective-C, no lo sé. Supongo que la
NSBundle
clase tendrá métodos que cargarán una clase específica.fuente