Objective-C hat keine Namespaces. Es ist ähnlich wie bei C, alles befindet sich in einem globalen Namespace. Es ist üblich, Klassen mit Initialen voranzustellen. Wenn Sie beispielsweise bei IBM arbeiten, können Sie ihnen "IBM" voranstellen. Wenn Sie für Microsoft arbeiten, können Sie "MS" verwenden. und so weiter. Manchmal beziehen sich die Initialen auf das Projekt, z. B. Adium-Präfixklassen mit "AI" (da keine Firma dahinter steht, könnten Sie die Initialen nehmen). Apple stellt Klassen mit NS voran und sagt, dass dieses Präfix nur für Apple reserviert ist.
So weit so gut. Das Anhängen von 2 bis 4 Buchstaben an einen vorangestellten Klassennamen ist jedoch ein sehr, sehr begrenzter Namespace. Beispielsweise könnten MS oder KI eine völlig andere Bedeutung haben (KI könnte beispielsweise künstliche Intelligenz sein), und ein anderer Entwickler könnte sich dafür entscheiden, sie zu verwenden und eine gleichnamige Klasse zu erstellen. Bang , Namespace-Kollision.
Okay, wenn dies eine Kollision zwischen einer Ihrer eigenen Klassen und einem externen Framework ist, das Sie verwenden, können Sie die Benennung Ihrer Klasse leicht ändern, keine große Sache. Aber was ist, wenn Sie zwei externe Frameworks verwenden, beide Frameworks, für die Sie keine Quelle haben und die Sie nicht ändern können? Ihre Anwendung ist mit beiden verknüpft, und Sie erhalten Namenskonflikte. Wie würden Sie diese lösen? Was ist der beste Weg, um sie so zu umgehen, dass Sie immer noch beide Klassen verwenden können?
In C können Sie diese umgehen, indem Sie nicht direkt mit der Bibliothek verknüpfen. Stattdessen laden Sie die Bibliothek zur Laufzeit mit dlopen (), suchen das gesuchte Symbol mit dlsym () und weisen es einem globalen Symbol zu (das Sie kann beliebig benennen) und dann über dieses globale Symbol darauf zugreifen. Wenn Sie beispielsweise einen Konflikt haben, weil eine C-Bibliothek eine Funktion mit dem Namen open () hat, können Sie eine Variable mit dem Namen myOpen definieren und auf die open () -Funktion der Bibliothek verweisen lassen, wenn Sie also das System open () verwenden möchten. Verwenden Sie einfach open () und wenn Sie das andere verwenden möchten, greifen Sie über die myOpen-ID darauf zu.
Ist in Objective-C etwas Ähnliches möglich, und wenn nicht, gibt es eine andere clevere, knifflige Lösung, mit der Sie Namespace-Konflikte lösen können? Irgendwelche Ideen?
Aktualisieren:
Nur um dies zu verdeutlichen: Antworten, die vorschlagen, wie Namespace-Kollisionen im Voraus vermieden oder ein besserer Namespace erstellt werden können, sind auf jeden Fall willkommen. Ich werde sie jedoch nicht als Antwort akzeptieren, da sie mein Problem nicht lösen. Ich habe zwei Bibliotheken und ihre Klassennamen kollidieren. Ich kann sie nicht ändern; Ich habe keine Quelle von beiden. Die Kollision ist bereits da und Tipps, wie sie im Voraus hätte vermieden werden können, helfen nicht mehr. Ich kann sie an die Entwickler dieser Frameworks weiterleiten und hoffe, dass sie in Zukunft einen besseren Namespace wählen, aber im Moment suche ich nach einer Lösung, um mit den Frameworks jetzt in einer einzigen Anwendung zu arbeiten. Irgendwelche Lösungen, um dies zu ermöglichen?
quelle
Antworten:
Wenn Sie nicht gleichzeitig Klassen aus beiden Frameworks verwenden müssen und auf Plattformen abzielen, die das Entladen von NSBundle unterstützen (OS X 10.4 oder höher, keine GNUStep-Unterstützung), und die Leistung für Sie wirklich kein Problem darstellt, glaube ich Sie können jedes Mal ein Framework laden, wenn Sie eine Klasse daraus verwenden müssen, und es dann entladen und das andere laden, wenn Sie das andere Framework verwenden müssen.
Meine ursprüngliche Idee war, mit NSBundle eines der Frameworks zu laden, dann die Klassen innerhalb dieses Frameworks zu kopieren oder umzubenennen und dann das andere Framework zu laden. Hierbei gibt es zwei Probleme. Erstens konnte ich keine Funktion zum Kopieren der Daten finden, auf die verwiesen werden soll, um eine Klasse umzubenennen oder zu kopieren, und alle anderen Klassen in diesem ersten Framework, die auf die umbenannte Klasse verweisen, verweisen jetzt auf die Klasse aus dem anderen Framework.
Sie müssten eine Klasse nicht kopieren oder umbenennen, wenn es eine Möglichkeit gäbe, die Daten zu kopieren, auf die ein IMP verweist. Sie können eine neue Klasse erstellen und dann über Ivars, Methoden, Eigenschaften und Kategorien kopieren. Viel mehr Arbeit, aber es ist möglich. Sie hätten jedoch immer noch ein Problem mit den anderen Klassen im Framework, die auf die falsche Klasse verweisen.
BEARBEITEN: Der grundlegende Unterschied zwischen der C- und der Objective-C-Laufzeit besteht meines Wissens darin, dass die Funktionen in diesen Bibliotheken beim Laden von Bibliotheken Zeiger auf Symbole enthalten, auf die sie verweisen, während sie in Objective-C Zeichenfolgendarstellungen der enthalten Namen dieser Symbole. In Ihrem Beispiel können Sie also dlsym verwenden, um die Adresse des Symbols im Speicher abzurufen und an ein anderes Symbol anzuhängen. Der andere Code in der Bibliothek funktioniert weiterhin, da Sie die Adresse des ursprünglichen Symbols nicht ändern. Objective-C verwendet eine Nachschlagetabelle, um Klassennamen Adressen zuzuordnen, und es handelt sich um eine 1-1-Zuordnung, sodass Sie nicht zwei Klassen mit demselben Namen haben können. Um beide Klassen zu laden, muss der Name einer von ihnen geändert werden. Wenn jedoch andere Klassen auf eine der Klassen mit diesem Namen zugreifen müssen,
quelle
Das Präfixieren Ihrer Klassen mit einem eindeutigen Präfix ist grundsätzlich die einzige Option, aber es gibt verschiedene Möglichkeiten, dies weniger belastend und hässlich zu machen. Es gibt eine lange Diskussion der Optionen hier . Mein Favorit ist die
@compatibility_alias
Objective-C-Compiler-Direktive ( hier beschrieben ). Sie können@compatibility_alias
eine Klasse "umbenennen", sodass Sie Ihre Klasse mit FQDN oder einem solchen Präfix benennen können:Als Teil einer vollständigen Strategie könnten Sie allen Ihren Klassen ein eindeutiges Präfix wie den FQDN voranstellen und dann einen Header mit allen erstellen
@compatibility_alias
(ich würde mir vorstellen, dass Sie diesen Header automatisch generieren könnten).Der Nachteil eines solchen Präfixes ist, dass Sie den wahren Klassennamen (z. B.
COM_WHATEVER_ClassName
oben) in alles eingeben müssen, was den Klassennamen aus einer Zeichenfolge neben dem Compiler benötigt. Insbesondere@compatibility_alias
handelt es sich um eine Compiler-Direktive, nicht um eine Laufzeitfunktion,NSClassFromString(ClassName)
die fehlschlägt (returnnil
) - Sie müssen sie verwendenNSClassFromString(COM_WHATERVER_ClassName)
. Sie könnenibtool
über die Erstellungsphase Klassennamen in einem Interface Builder nib / xib ändern, sodass Sie nicht den vollständigen COM_WHATEVER _... in Interface Builder schreiben müssen.Letzte Einschränkung: Da es sich um eine Compiler-Direktive handelt (und zwar um eine obskure), kann sie möglicherweise nicht über Compiler hinweg portiert werden. Insbesondere weiß ich nicht, ob es mit dem Clang-Frontend aus dem LLVM-Projekt funktioniert, obwohl es mit LLVM-GCC (LLVM mit dem GCC-Frontend) funktionieren sollte.
quelle
Einige Leute haben bereits einen kniffligen und cleveren Code geteilt, der zur Lösung des Problems beitragen könnte. Einige der Vorschläge mögen funktionieren, aber alle sind weniger als ideal, und einige sind geradezu unangenehm umzusetzen. (Manchmal sind hässliche Hacks unvermeidlich, aber ich versuche sie zu vermeiden, wann immer ich kann.) Aus praktischer Sicht sind hier meine Vorschläge.
Ich vermute, dass Lizenzgebühren, Bedingungen und Laufzeiten sofortige Maßnahmen in Bezug auf einen dieser Punkte verhindern können. Hoffentlich können Sie den Konflikt so schnell wie möglich lösen. Viel Glück!
quelle
Dies ist grob, aber Sie können verteilte Objekte verwenden, um eine der Klassen nur in einer untergeordneten Programmadresse und einem RPC zu behalten. Das wird chaotisch, wenn Sie eine Menge Dinge hin und her geben (und möglicherweise nicht möglich, wenn beide Klassen Ansichten usw. direkt manipulieren).
Es gibt andere mögliche Lösungen, aber viele davon hängen von der genauen Situation ab. Verwenden Sie insbesondere die modernen oder älteren Laufzeiten, sind Sie eine fette oder einzelne Architektur, 32 oder 64 Bit, auf welche Betriebssystemversionen zielen Sie ab, verknüpfen Sie dynamisch, statisch oder haben Sie die Wahl, und ist dies möglicherweise der Fall? Es ist in Ordnung, etwas zu tun, das möglicherweise Wartung für neue Software-Updates erfordert.
Wenn Sie wirklich verzweifelt sind, können Sie Folgendes tun:
Das Obige wird ziemlich arbeitsintensiv sein, und wenn Sie es gegen mehrere Bögen und verschiedene Laufzeitversionen implementieren müssen, wird es sehr unangenehm sein, aber es kann definitiv zum Funktionieren gebracht werden.
quelle
Haben Sie darüber nachgedacht, die Laufzeitfunktionen (/usr/include/objc/runtime.h) zu verwenden, um eine der widersprüchlichen Klassen in eine nicht kollidierende Klasse zu klonen und dann das Framework für kollidierende Klassen zu laden? (Dies würde erfordern, dass die kollidierenden Frameworks zu unterschiedlichen Zeiten geladen werden, um zu funktionieren.)
Sie können die Klassen ivars, Methoden (mit Namen und Implementierungsadressen) und Namen mit der Laufzeit überprüfen und Ihre eigenen dynamisch erstellen, um das gleiche ivar-Layout, die gleichen Methodennamen / Implementierungsadressen und nur den Namen zu erhalten (um das zu vermeiden) Kollision)
quelle
Verzweifelte Situationen erfordern verzweifelte Maßnahmen. Haben Sie darüber nachgedacht, den Objektcode (oder die Bibliotheksdatei) einer der Bibliotheken zu hacken und das kollidierende Symbol in einen alternativen Namen zu ändern - von gleicher Länge, aber unterschiedlicher Schreibweise (aber Empfehlung, gleiche Namenslänge)? Von Natur aus böse.
Es ist nicht klar, ob Ihr Code die beiden Funktionen direkt mit demselben Namen, aber unterschiedlichen Implementierungen aufruft oder ob der Konflikt indirekt ist (und es ist auch nicht klar, ob er einen Unterschied macht). Es besteht jedoch zumindest eine externe Chance, dass das Umbenennen funktioniert. Es könnte auch eine Idee sein, den Unterschied in der Schreibweise zu minimieren, damit die Umbenennung die Dinge nicht aus der Reihenfolge bringt, wenn die Symbole in einer Tabelle in einer sortierten Reihenfolge vorliegen. Dinge wie die binäre Suche werden verärgert, wenn das Array, das sie suchen, nicht wie erwartet in sortierter Reihenfolge sortiert ist.
quelle
@compatibility_alias
wird in der Lage sein, Klassennamensraumkonflikte zu lösen, zDadurch werden jedoch keine Aufzählungen, Typedefs oder Protokollnamespace-Kollisionen behoben . Darüber hinaus spielt es nicht gut mit
@class
Vorwärtsdeklarationen der ursprünglichen Klasse. Da die meisten Frameworks mit diesen nicht klassenbezogenen Dingen wie typedefs geliefert werden, können Sie das Namespace-Problem wahrscheinlich nicht nur mit compatible_alias beheben.Ich habe mir ein ähnliches Problem wie Ihres angesehen , aber ich hatte Zugriff auf die Quelle und baute die Frameworks auf. Die beste Lösung, die ich dafür gefunden habe, war die
@compatibility_alias
bedingte Verwendung von #defines zur Unterstützung der Aufzählungen / typedefs / protocol / etc. Sie können dies unter bestimmten Bedingungen auf der Kompilierungseinheit für den betreffenden Header tun, um das Risiko zu minimieren, dass Inhalte im anderen kollidierenden Framework erweitert werden.quelle
Es scheint, dass das Problem darin besteht, dass Sie nicht auf Header-Dateien von beiden Systemen in derselben Übersetzungseinheit (Quelldatei) verweisen können. Wenn Sie Objective-C-Wrapper um die Bibliotheken erstellen (wodurch sie im Prozess benutzerfreundlicher werden) und nur # die Header für jede Bibliothek in die Implementierung der Wrapper-Klassen einbeziehen, werden Namenskollisionen effektiv getrennt.
Ich habe nicht genug Erfahrung damit in Ziel-C (gerade erst angefangen), aber ich glaube, das würde ich in C tun.
quelle
Das Präfixieren der Dateien ist die einfachste Lösung, die mir bekannt ist. Cocoadev hat eine Namespace-Seite, die eine Community-Aktion ist, um Namespace-Kollisionen zu vermeiden. Fühlen Sie sich frei, Ihre eigenen zu dieser Liste hinzuzufügen, ich glaube, das ist, was es ist.
http://www.cocoadev.com/index.pl?ChooseYourOwnPrefix
quelle
Wenn Sie eine Kollision haben, würde ich vorschlagen, dass Sie sich überlegen, wie Sie eines der Frameworks aus Ihrer Anwendung heraus umgestalten könnten. Eine Kollision deutet darauf hin, dass die beiden ähnliche Dinge tun wie sie ist, und Sie könnten wahrscheinlich ein zusätzliches Framework verwenden, indem Sie einfach Ihre Anwendung umgestalten. Dies würde nicht nur Ihr Namespace-Problem lösen, sondern auch Ihren Code robuster, einfacher zu warten und effizienter machen.
Bei einer technischeren Lösung wäre dies meine Wahl, wenn ich in Ihrer Position wäre.
quelle
Wenn sich die Kollision nur auf der Ebene der statischen Verknüpfung befindet, können Sie auswählen, welche Bibliothek zum Auflösen von Symbolen verwendet wird:
Wenn
foo.o
undbar.o
beide Referenz das Symbolrat
dannlibdog
wird lösenfoo.o
'srat
undlibcat
wird behebenbar.o
istrat
.quelle
Nur ein Gedanke ... nicht getestet oder bewiesen und könnte ein Zeichen sein, aber haben Sie darüber nachgedacht, einen Adapter für die von Ihnen verwendeten Klassen aus dem einfacheren Framework zu schreiben ... oder zumindest deren Schnittstellen?
Wenn Sie einen Wrapper um das einfachere Framework schreiben würden (oder um die Schnittstellen, auf die Sie am wenigsten zugreifen), wäre es nicht möglich, diesen Wrapper in eine Bibliothek zu kompilieren. Da die Bibliothek vorkompiliert ist und nur ihre Header verteilt werden müssen, würden Sie das zugrunde liegende Framework effektiv ausblenden und es mit dem zweiten Framework kombinieren können, wenn es zu Konflikten kommt.
Ich weiß natürlich zu schätzen, dass es wahrscheinlich Zeiten gibt, in denen Sie Klassen aus beiden Frameworks gleichzeitig verwenden müssen. Sie könnten jedoch Fabriken für weitere Klassenadapter dieses Frameworks bereitstellen. Auf der Rückseite dieses Punktes müssten Sie wahrscheinlich ein wenig umgestalten, um die von Ihnen verwendeten Schnittstellen aus beiden Frameworks zu extrahieren. Dies sollte ein guter Ausgangspunkt für die Erstellung Ihres Wrappers sein.
Sie können auf die Bibliothek aufbauen, wenn Sie weitere Funktionen aus der umschlossenen Bibliothek benötigen, und sie einfach neu kompilieren, wenn Sie sie ändern.
Wiederum in keiner Weise bewiesen, aber ich wollte eine Perspektive hinzufügen. ich hoffe es hilft :)
quelle
Wenn Sie zwei Frameworks mit demselben Funktionsnamen haben, können Sie versuchen, die Frameworks dynamisch zu laden. Es wird unelegant sein, aber möglich. Wie es mit Objective-C-Klassen geht, weiß ich nicht. Ich vermute, die
NSBundle
Klasse wird Methoden haben, die eine bestimmte Klasse laden.quelle