174 votes

Quelle est la meilleure façon de résoudre une collision de noms Objective-C ?

Objective-C n'a pas d'espaces de noms; c'est un peu comme le C, le tout est dans un espace de noms global. La pratique courante est de préfixe des classes avec des initiales, par exemple, si vous travaillez au sein d'IBM, vous pouvez préfixer avec "IBM"; si vous travaillez pour Microsoft, vous pouvez utiliser "MS"; et ainsi de suite. Parfois les initiales reportez-vous à ce projet, par exemple Adium préfixes de classes avec "AI" (comme il n'y a pas d'entreprise derrière elle, ce que l'on pouvait prendre les initiales). Apple préfixes de classes avec NS et dit ce préfixe est réservé pour Apple uniquement.

C'est très bien. Mais en ajoutant 2 à 4 lettres pour un nom de classe à l'avant est un très, très peu d'espace de noms. E. g. MS ou IA pourrait avoir un tout autre sens (IA pourrait être l'Intelligence Artificielle par exemple) et quelques autres développeur peut décider de les utiliser et de créer un même nom de la classe. Bang, l'espace de noms de collision.

Ok, si c'est une collision entre un de vos propres classes et un de un cadre externe que vous utilisez, vous pouvez facilement changer la dénomination de votre classe, pas une grosse affaire. Mais que faire si vous utilisez deux cadres, deux cadres que vous n'avez pas la source et que vous ne pouvez pas changer? Votre demande de liens avec les deux et vous obtenez des conflits de nom. Comment feriez-vous pour la résolution de ces? Quelle est la meilleure façon de travailler autour d'eux, de telle sorte que vous pouvez toujours utiliser les deux classes?

En C, vous pouvez contourner ces par ne pas relier directement à la bibliothèque, au lieu de charger la bibliothèque au moment de l'exécution, à l'aide de dlopen(), puis de trouver le symbole que vous recherchez à l'aide de dlsym() et de l'affecter à un symbole mondial (que vous pouvez nommer comme vous le souhaitez), puis accès par le biais de ce symbole mondial. E. g. si vous avez un conflit, parce que certains de la bibliothèque C a une fonction nommée open(), vous pouvez définir une variable nommée myOpen et de pointer à la fonction open() de la bibliothèque, ainsi, lorsque vous souhaitez utiliser le système open(), il suffit d'utiliser open() et lorsque vous souhaitez utiliser l'autre, vous y accédez via le myOpen identificateur.

Est quelque chose de semblable possible en Objective-C et si non, est-il un autre intelligent, rusé solution, vous pouvez utiliser de résoudre les conflits d'espace de noms? Des idées?


Mise à jour:

Juste pour clarifier ce: les réponses qui suggèrent comment éviter les collisions d'espace de noms à l'avance ou comment créer un meilleur espace de noms sont certainement les bienvenus; cependant, je ne vais pas accepter que la réponse , car ils ne permettent pas de résoudre mon problème. J'ai deux bibliothèques et leurs noms de classe entrent en collision. Je ne peux pas les changer, je n'ai pas la source de l'un des deux. La collision est déjà là et des conseils sur comment cela aurait pu être évité à l'avance ne sert plus à rien. Je peux transmettre les développeurs de ces cadres et de l'espoir qu'ils choisissent un meilleur espace de noms dans le futur, mais pour le moment, je suis à la recherche d'une solution de travail avec les cadres de la droite maintenant dans une seule application. Toutes les solutions pour rendre cela possible?

93voto

Barry Wark Points 73462

La préfixation de vos classes avec un préfixe unique est fondamentalement la seule option, mais il y a plusieurs façons de faire ce moins onéreuse et plus laid. Il y a une longue discussion des options ici. Mon préféré est le @compatibility_alias Objectif-C de la directive du compilateur (décrit ici). Vous pouvez utiliser @compatibility_alias "renommer" d'une classe, vous permettant de nom de votre classe à l'aide de nom de domaine complet ou un préfixe:

@interface COM_WHATEVER_ClassName : NSObject
@end

@compatibility_alias ClassName COM_WHATEVER_ClassName
// now ClassName is an alias for COM_WHATEVER_ClassName

@implementation ClassName //OK
//blah
@end

ClassName *myClass; //OK

Dans le cadre d'une stratégie complète, vous pouvez préfixer toutes vos classes avec un préfixe unique tels que le nom de domaine complet et ensuite créer un en-tête avec tous les @compatibility_alias (j'imagine que vous pourriez générer automatiquement le dit d'en-tête).

L'inconvénient de la préfixation comme cela, c'est que vous avez à entrer dans le véritable nom de la classe (par exemple, COM_WHATEVER_ClassName ci-dessus) dans tout ce qui doit être le nom de la classe à partir d'une chaîne en outre le compilateur. Notamment, @compatibility_alias est une directive de compilation, et non pas à l'exécution de la fonction, de sorte NSClassFromString(ClassName) échouent (retour nil)--vous devrez utiliser NSClassFromString(COM_WHATERVER_ClassName). Vous pouvez utiliser ibtool via phase de construction de modifier les noms de classe dans une Interface Builder nib/xib, de sorte que vous n'avez pas à écrire le plein COM_WHATEVER_... dans Interface Builder.

Avertissement Final: parce que c'est une directive de compilation (et un obscur), il peut ne pas être portable sur l'ensemble des compilateurs. En particulier, je ne sais pas si cela fonctionne avec le Bruit frontend de la LLVM projet, même s'il devrait travailler avec LLVM-GCC (LLVM à l'aide de la GCC frontend).

47voto

Michael Buckley Points 2856

Si vous n'avez pas besoin d'utiliser des classes de deux cadres, dans le même temps, et vous ciblez les plates-formes qui supportent NSBundle de déchargement (OS X 10.4 ou version ultérieure, pas de GNUStep de soutien), et le rendement n'est vraiment pas un problème pour vous, je crois que vous pouvez charger un cadre à chaque fois que vous devez utiliser une classe à partir d'elle, puis le décharger et charger l'autre quand vous avez besoin d'utiliser l'autre cadre.

Mon idée initiale était d'utiliser NSBundle de charger l'un des cadres, puis les copier ou renommer les classes à l'intérieur de ce cadre, et ensuite chargez l'autre cadre. Il y a deux problèmes avec cette. Tout d'abord, je ne pouvais pas trouver une fonction pour copier les données de relevé pour renommer ou copier une classe, et toutes les autres classes dans le premier cadre de référence de la renommé de la classe désormais référence à la classe de l'autre cadre.

Vous n'avez pas besoin de copier ou renommer une classe, si il y avait un moyen de copier les données pointées par un IMP. Vous pouvez créer une nouvelle classe et puis les copier sur ivars, les méthodes, les propriétés et les catégories. Beaucoup plus de travail, mais c'est possible. Cependant, vous avez toujours un problème avec les autres classes dans le cadre du référencement de la mauvaise classe.

EDIT: La différence fondamentale entre le C et Objective-C temps de fonctionnement est, comme je le comprends, quand les bibliothèques sont chargées, les fonctions dans les bibliothèques contiennent des pointeurs vers les symboles de référence, alors qu'en Objective-C, ils contiennent des représentations de chaîne de noms de thsoe symboles. Ainsi, dans votre exemple, vous pouvez utiliser dlsym d'obtenir le symbole de l'adresse dans la mémoire et de l'attacher à un autre symbole. L'autre code dans la bibliothèque fonctionne toujours parce que vous n'êtes pas changer l'adresse du symbole d'origine. Objective-C utilise une table de choix à la carte les noms de classe d'adresses, et c'est 1-1 de la cartographie, de sorte que vous ne pouvez pas avoir deux classes portant le même nom. Ainsi, pour charger les deux classes, l'une d'entre elles doivent avoir leur nom a changé. Toutefois, lorsque d'autres classes ont besoin pour accéder à l'une des classes de même nom, ils vont demander à la table de recherche pour son adresse, et la table de recherche ne sera jamais de retour l'adresse de la renommée classe de la classe d'origine de son nom.

12voto

Quinn Taylor Points 29688

Plusieurs personnes ont déjà partagé quelques délicate et intelligente de code qui pourrait aider à résoudre le problème. Certaines de ces suggestions peuvent travailler, mais ils sont tous de moins que l'idéal, et certains d'entre eux sont carrément méchant, à mettre en œuvre. (Parfois laid hacks sont inévitables, mais j'essaie de les éviter dès que je peux.) À partir d'un point de vue pratique, ici sont mes suggestions.

  1. En tout cas, en informer les développeurs de deux cadres du conflit, et il est clair que leur incapacité à éviter et/ou de traiter avec elle vous cause des problèmes d'affaires, ce qui pourrait se traduire en perte de chiffre d'affaires si le problème persiste. Soulignons que lors de la résolution des conflits existants sur une fonction de la classe est la moins intrusive corriger, de changer leur préfixe entièrement (ou en utilisant un si ils ne le sont pas actuellement, et honte à eux!) est la meilleure façon de s'assurer qu'ils ne verront pas le même problème à nouveau.
  2. Si les conflits de nommage sont limités à une taille raisonnable, un ensemble de classes, voir si vous pouvez travailler autour de ces classes, en particulier si un conflit de classes n'est pas utilisé par votre code, directement ou indirectement. Si oui, à voir si le fournisseur fournira une version personnalisée du cadre qui ne comprennent pas les conflits de classes. Si non, franchement, le fait que leur manque de souplesse est de réduire votre ROI de l'utilisation de leur cadre. Ne vous sentez pas mal à être arrogant au sein de la raison - le client a toujours raison. ;-)
  3. Si un cadre est plus "dispensable", vous pourriez envisager de le remplacer par un autre cadre (ou une combinaison de code), soit à un tiers ou à un homebrew. (Ce dernier n'est pas souhaitable pour le pire des cas, car il va certainement entraîner des coûts des entreprises, à la fois pour le développement et l'entretien.) Si vous le faites, d'informer le vendeur de qui cadre exactement pourquoi vous avez décidé de ne pas utiliser leur cadre.
  4. Si les deux cadres sont jugés tout aussi indispensable à votre demande, à explorer des façons de facteur d'utilisation de l'un d'eux à un ou plusieurs processus distincts, peut-être, de la communication par FAIRE comme Louis Gerbarg suggéré. Selon le degré de communication, cela peut ne pas être aussi mauvais que vous le pensez. Plusieurs programmes (y compris QuickTime, je crois) utiliser cette approche pour fournir plus de sécurité granulaires fourni par l'aide de la Ceinture de sécurité sandbox profils de Léopard, de sorte que seul un sous-ensemble spécifique de votre code est autorisé à effectuer des critiques ou à des opérations délicates. Les performances seront un compromis, mais peut-être votre seule option

Je suppose que les frais de licence, les conditions et les durées peuvent empêcher l'action instantanée sur aucun de ces points. Nous espérons que vous serez en mesure de résoudre le conflit dès que possible. Bonne chance!

8voto

Louis Gerbarg Points 33025

C'est brut, mais vous pouvez utiliser les objets distribués afin de garder l'une des classes que dans un subordonné programmes de l'adresse et de la RPC. Qui va être gênant si vous êtes de passage à une tonne de trucs en arrière et en avant (et peut ne pas être possible si les deux de la classe sont manipulant directement les points de vue, etc).

Il y a d'autres solutions possibles, mais beaucoup d'entre eux dépendent de la situation exacte. En particulier, êtes-vous à l'aide de l'modernes ou plus anciens temps de fonctionnement, êtes-vous de la graisse ou de l'architecture unique, 32 ou 64 bits, quel système d'exploitation des rejets ciblez-vous, êtes-vous de manière dynamique de la liaison, la liaison statique, ou avez-vous un choix, et est-il potentiellement d'accord pour faire quelque chose qui peut nécessiter de l'entretien pour les nouvelles mises à jour du logiciel.

Si vous êtes vraiment désespéré, ce que vous pourriez faire est:

  1. Pas de lien à l'encontre de l'une des bibliothèques directement
  2. Mettre en œuvre une autre version de l'objc runtime routines qui change de nom au moment du chargement (la caisse de la objc4 projet, qu'est-ce exactement ce que vous devez faire dépend d'un certain nombre de questions que j'ai posées ci-dessus, mais il devrait être possible, peu importe ce que les réponses sont).
  3. Utilisez quelque chose comme mach_override afin d'injecter votre nouvelle mise en œuvre
  4. Chargement de la nouvelle bibliothèque à l'aide de méthodes normales, il faudra passer par le correctif de l'éditeur de liens de routine et obtenir son className changé

Le ci-dessus est très intensive en main, et si vous avez besoin de la mettre en œuvre à l'encontre de plusieurs architectures et les différentes versions du moteur d'exécution, il sera très désagréable, mais il peut certainement être fait au travail.

4voto

xtophyr Points 151

Avez-vous envisagé d'utiliser le moteur d'exécution des fonctions (/usr/include/objc/runtime.h) pour cloner un conflit de classes pour les non-collision de la classe, puis le chargement de la collision cadre de la classe? (cela nécessiterait la collision des cadres chargés à différentes heures de travail.)

Vous pouvez inspecter les classes, ivars, les méthodes (avec les noms et la mise en œuvre des adresses) et les noms avec l'environnement d'exécution, et de créer votre propre ainsi dynamiquement pour avoir le même ivar mise en page, les méthodes de noms/mise en œuvre des adresses, et ne diffèrent que par le nom (pour éviter la collision)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X