Jaki jest najlepszy sposób na rozwiązanie kolizji przestrzeni nazw Objective-C?

Objective-C nie ma przestrzeni nazw; jest podobny do C, wszystko znajduje się w jednej globalnej przestrzeni nazw. Jeśli pracujesz w IBM, możesz przedrostek "IBM"; jeśli pracujesz dla Microsoft, możesz użyć "MS"; i tak dalej. Czasami Inicjały odnoszą się do projektu, np. Adium prefiksy klas z "AI" (ponieważ nie ma za nim firmy, która mogłaby wziąć Inicjały). Apple prefiks klas z NS i mówi, że ten prefiks jest zarezerwowany tylko dla Apple.

Na razie tak dobrze. Ale dołączanie 2 do 4 liter do nazwy klasy z przodu jest bardzo, bardzo ograniczoną przestrzenią nazw. Np. MS lub AI mogą mieć zupełnie inne znaczenie (AI może być na przykład sztuczną inteligencją), a inny programista może zdecydować się na ich użycie i utworzenie równie nazwanej klasy. Bang , namespace collision.

OK, jeśli jest to kolizja między jedną z Twoich klas i jedną z zewnętrznych frameworków, których używasz, możesz łatwo Zmień nazwę swojej klasy, nic wielkiego. ale co jeśli użyjesz dwóch zewnętrznych frameworków, obu frameworków, do których nie masz źródła i których nie możesz zmienić? Twoja aplikacja łączy się z nimi i dostajesz konflikty nazw. Jak byś to rozwiązał? Jaki jest najlepszy sposób obejścia ich w taki sposób, że można nadal korzystać z obu klas?

W C możesz obejść je nie linkując bezpośrednio do biblioteki, zamiast tego ładujesz bibliotekę w runtime, używając dlopen(), następnie znajdź symbol, którego szukasz za pomocą dlsym() i przypisz go do symbolu globalnego (który możesz nazwać w dowolny sposób), a następnie uzyskaj do niego dostęp za pomocą tego symbolu globalnego. Na przykład, jeśli masz konflikt, ponieważ jakaś biblioteka C ma funkcję o nazwie open (), możesz zdefiniować zmienną o nazwie myOpen i skierować ją do funkcji open() biblioteki, więc gdy chcesz użyć systemowej metody open(), po prostu używasz metody open (), a gdy chcesz użyć drugiej, uzyskujesz do niej dostęp poprzez identyfikator myOpen.

Czy coś podobnego jest możliwe w Objective-C, a jeśli nie, czy jest jakieś inne sprytne, trudne rozwiązanie, które można użyć do rozwiązywania konfliktów przestrzeni nazw? Jakieś pomysły?


Aktualizacja:

Aby to wyjaśnić: odpowiedzi, które sugerują, jak uniknąć kolizji przestrzeni nazw z góry lub jak stworzyć lepszą przestrzeń nazw są z pewnością mile widziane; jednak nie przyjmę ich jako odpowiedź , ponieważ nie rozwiązują mojego problemu. Mam dwie biblioteki i ich nazwy klas zderzają się. Nie mogę ich zmienić; nie mam źródła żadnego z nich. Kolizja już istnieje i wskazówki, jak można było jej uniknąć z wyprzedzeniem, już nie pomogą. Mogę przekazać je programistom tych frameworków i mam nadzieję, że w przyszłości wybiorą lepszą przestrzeń nazw, ale na razie szukam rozwiązania do pracy z frameworkami w ramach jednej aplikacji. Jakieś rozwiązania, które to umożliwią?

Author: Jonas, 2008-10-07

13 answers

Jeśli nie musisz używać klas z obu frameworków w tym samym czasie, a kierujesz się na platformy, które obsługują rozładowywanie NSBundle (OS X 10.4 lub nowszy, bez obsługi GNUStep), a wydajność naprawdę nie jest dla Ciebie problemem, wierzę, że możesz załadować jedną framework za każdym razem, gdy musisz użyć z niej klasy, a następnie rozładować ją i załadować drugą, gdy musisz użyć drugiej framework.

Moim początkowym pomysłem było użycie NSBundle do załadowania jednego z frameworków, a następnie skopiowanie lub Zmień nazwy klas wewnątrz tego frameworka, a następnie załaduj drugi framework. Są z tym dwa problemy. Po pierwsze, nie mogłem znaleźć funkcji kopiującej dane wskazujące na zmianę nazwy lub skopiowanie klasy, a wszelkie inne klasy w tym pierwszym frameworku, które odwołują się do przemianowanej klasy, będą teraz odwoływać się do klasy z innego frameworku.

Nie musiałbyś kopiować ani zmieniać nazwy klasy, gdyby istniał sposób na skopiowanie danych wskazywanych przez IMP. Możesz utworzyć nową klasę, A następnie skopiować ivars, metody, właściwości i kategorie. Dużo więcej pracy, ale jest to możliwe. Jednak nadal będziesz miał problem z innymi klasami w ramach odwołujących się do niewłaściwej klasy.

EDIT: zasadnicza różnica między środowiskami uruchomieniowymi C i Objective-C polega na tym, że gdy biblioteki są ładowane, funkcje w tych bibliotekach zawierają wskaźniki do dowolnych symboli, do których się odwołują, podczas gdy w Objective-C zawierają reprezentacje łańcuchowe nazw symboli thsoe. Tak więc w przykładzie można użyć dlsym, aby uzyskać adres symbolu w pamięci i dołączyć go do innego symbolu. Drugi kod w bibliotece nadal działa, ponieważ nie zmieniasz adresu oryginalnego symbolu. Objective - C używa tabeli wyszukiwania do mapowania nazw klas na adresy, a jest to mapowanie 1-1, więc nie można mieć dwóch klas o tej samej nazwie. Tak więc, aby załadować obie klasy, jedna z nich musi mieć zmienioną nazwę. Jednak, gdy inne klasy muszą uzyskać dostęp do jednej z klas z tym nazwa, zapytają tabelę Wyszukiwania o jej adres, a tabela wyszukiwania nigdy nie zwróci adresu przemianowanej klasy, biorąc pod uwagę oryginalną nazwę klasy.

 47
Author: Michael Buckley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-08 05:25:08

Prefiksowanie klas unikalnym prefiksem jest zasadniczo jedyną opcją, ale istnieje kilka sposobów, aby uczynić to mniej uciążliwym i brzydkim. Istnieje długa dyskusja na temat opcji tutaj . Moja ulubiona jest dyrektywa kompilatora @compatibility_alias Objective-C (opisana tutaj ). Możesz użyć @compatibility_alias do "zmiany nazwy" klasy, pozwalając na nazwanie klasy za pomocą FQDN lub jakiegoś takiego prefiksu:

@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

W ramach kompletnej strategii, można przedrostek wszystkich klas z unikalnym prefiks taki jak FQDN, a następnie utwórz nagłówek ze wszystkimi @compatibility_alias (wyobrażam sobie, że możesz automatycznie wygenerować wspomniany nagłówek).

Minusem takiego prefiksu jest to, że musisz wprowadzić prawdziwą nazwę klasy (np. COM_WHATEVER_ClassName powyżej) we wszystkim, co wymaga nazwy klasy z łańcucha znaków oprócz kompilatora. W szczególności @compatibility_alias jest dyrektywą kompilatora, a nie funkcją runtime, więc NSClassFromString(ClassName) nie powiedzie się (return nil) -- będziesz musiał użyć NSClassFromString(COM_WHATERVER_ClassName). Możesz użyć ibtool w fazie budowania, aby zmodyfikować nazwy klas w Interface Builder nib / xib, abyś nie musiał pisać pełnego COM_WHATEVER_... w Interface Builder.

Ostatnie zastrzeżenie: ponieważ jest to dyrektywa kompilatora (i w tym niejasna), może nie być przenośna w różnych kompilatorach. W szczególności, Nie wiem, czy działa z nakładką clang z projektu LLVM, chociaż powinna działać z LLVM-GCC (LLVM używając nakładki GCC).

 92
Author: Barry Wark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-24 16:06:15

Kilka osób już podzieliło się skomplikowanym i sprytnym kodem, który może pomóc rozwiązać problem. Niektóre sugestie mogą działać, ale wszystkie z nich są mniej niż idealne, a niektóre z nich są wręcz paskudne do wdrożenia. (Czasami brzydkie hacki są nieuniknione, ale staram się ich unikać, kiedy tylko mogę.) Z praktycznego punktu widzenia, oto moje sugestie.

  1. w każdym razie poinformuj deweloperów obu frameworków o konflikcie i wyjaśnij, że ich brak unikaj i / lub radź sobie z nim, powodując prawdziwe problemy biznesowe, które mogą przełożyć się na utracone przychody biznesowe, jeśli zostaną nierozwiązane. Podkreśl, że chociaż rozwiązywanie istniejących konfliktów w zależności od klasy jest mniej inwazyjną poprawką, zmiana ich prefiksu całkowicie (lub użycie go, jeśli nie jest obecnie, i wstydź się ich!) jest najlepszym sposobem, aby upewnić się, że nie zobaczą ten sam problem ponownie.
  2. Jeśli konflikty nazw są ograniczone do dość małego zestawu klas, sprawdź, czy możesz obejść tylko te klasy, zwłaszcza jeśli jedna ze sprzecznych klas nie jest używana przez Twój kod, bezpośrednio lub pośrednio. Jeśli tak, sprawdź, czy dostawca dostarczy niestandardową wersję frameworka, która nie zawiera klas kolidujących ze sobą. Jeśli nie, bądź szczery na temat faktu, że ich nieelastyczność zmniejsza ROI z korzystania z ich RAM. Nie czuj się źle z powodu bycia nachalnym w granicach rozsądku - Klient ma zawsze rację. ;-)
  3. Jeśli jeden framework jest bardziej "zbędny", możesz rozważ zastąpienie go innym frameworkiem (lub kombinacją kodu), zewnętrznym lub homebrew. (Ten ostatni jest niepożądanym najgorszym przypadkiem, ponieważ z pewnością pociągnie za sobą dodatkowe koszty biznesowe, zarówno związane z rozwojem, jak i utrzymaniem.) Jeśli to zrobisz, poinformuj sprzedawcę tego frameworka dokładnie, dlaczego zdecydowałeś się nie używać ich frameworka.
  4. Jeśli obie frameworki są uważane za równie niezbędne dla Twojej aplikacji, zbadaj sposoby na uwzględnienie użycia jednej z nich w jednej lub kilku osobnych procesy, być może komunikowanie się za pośrednictwem DO, jak zasugerował Louis Gerbarg. W zależności od stopnia komunikacji, może to nie być tak złe, jak można się spodziewać. Kilka programów (w tym, jak sądzę, QuickTime) używa tego podejścia, aby zapewnić bardziej szczegółowe bezpieczeństwo dzięki użyciu profili sandbox W Leopardzie, tak aby tylko określony podzbiór kodu mógł wykonywać krytyczne lub wrażliwe operacje. Wydajność będzie kompromitacją, ale może być twoim jedynym opcja

Zgaduję, że opłaty licencyjne, warunki i czas trwania mogą uniemożliwić natychmiastowe działanie w którymkolwiek z tych punktów. Mam nadzieję, że uda ci się jak najszybciej rozwiązać konflikt. Powodzenia!

 12
Author: Quinn Taylor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 17:43:34

To jest obrzydliwe, ale możesz użyć rozproszonych obiektów , aby zachować jedną z klas tylko w podrzędnym adresie programów i RPC do niego. Zrobi się bałagan, jeśli przekazujesz mnóstwo rzeczy tam i z powrotem (i może nie być to możliwe, jeśli obie klasy bezpośrednio manipulują widokami itp.).

Są inne potencjalne rozwiązania, ale wiele z nich zależy od konkretnej sytuacji. W szczególności, czy korzystasz z nowoczesnych lub starszych runtimes, czy jesteś gruby lub samotny Architektura, 32 lub 64-bitowa, jakie wydania systemu operacyjnego kierujesz, czy dynamicznie łączysz, statycznie łączysz, czy masz wybór i czy potencjalnie możesz zrobić coś, co może wymagać konserwacji dla nowych aktualizacji oprogramowania.

Jeśli jesteś naprawdę zdesperowany, to co możesz zrobić to:

  1. nie linkować bezpośrednio do jednej z bibliotek
  2. zaimplementuj alternatywną wersję funkcji runtime ObjC, która zmienia nazwę w czasie ładowania (checkout the objc4 projekt, co dokładnie trzeba zrobić, zależy od wielu pytań, które zadałem powyżej, ale powinno być możliwe bez względu na to, jakie są odpowiedzi).
  3. użyj czegoś w rodzaju mach_override aby wprowadzić nową implementację
  4. załaduj nową bibliotekę przy użyciu normalnych metod, przejdzie ona przez załataną procedurę linkera i zmieni jej nazwę klasy

Powyższe będzie dość pracochłonne, a jeśli trzeba je wdrożyć przeciwko wiele archów i różne wersje uruchomieniowe będzie to bardzo nieprzyjemne, ale na pewno może być wykonane do pracy.

 8
Author: Louis Gerbarg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-28 00:00:52

Czy rozważałeś użycie funkcji runtime (/usr/include/objc / runtime.h) sklonować jedną ze sprzecznych klas do klasy nie kolidującej, a następnie załadować Framework klasy kolidującej? (wymagałoby to załadowania zderzających się ram w różnym czasie do pracy.)

Możesz sprawdzać klasy ivars, metody (z nazwami i adresami implementacji) i nazwy za pomocą runtime, a także tworzyć własne dynamicznie, aby mieć ten sam układ ivar, metody nazwy / adresy implementacji i różnią się tylko nazwami (aby uniknąć kolizji)

 4
Author: xtophyr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-07 18:35:53

Desperackie sytuacje wymagają desperackich środków. Czy rozważałeś włamanie się do kodu obiektowego (lub pliku bibliotecznego) jednej z bibliotek, zmianę symbolu kolizji na alternatywną nazwę - o tej samej długości, ale innej pisowni (ale, rekomendacja, ta sama długość nazwy)? Z natury paskudny.

Nie jest jasne, czy Twój kod bezpośrednio wywołuje dwie funkcje o tej samej nazwie, ale różne implementacje, czy też konflikt jest pośredni (nie jest też jasne, czy robi różnicę). Istnieje jednak przynajmniej zewnętrzna szansa, że zmiana nazwy zadziała. Może być też pomysłem, aby zminimalizować różnicę w pisowni, tak aby jeśli symbole są uporządkowane w tabeli, zmiana nazwy nie przesuwa rzeczy w porządku. Rzeczy takie jak wyszukiwanie binarne denerwują się, jeśli tablica, którą szukają, nie jest uporządkowana zgodnie z oczekiwaniami.

 3
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-27 07:49:06

Wydaje się, że problem polega na tym, że nie można odwoływać się do plików nagłówków z obu systemów w tej samej jednostce tłumaczenia (pliku źródłowego). Jeśli utworzysz wokół bibliotek Objective-c wrappery (czyniąc je bardziej użytecznymi w procesie) i tylko #Dołącz nagłówki dla każdej biblioteki w implementacji klas wrapperów, to efektywnie oddzieliłoby to kolizje nazw.

Nie mam wystarczającego doświadczenia z tym w objective-c (dopiero zaczynam), ale wierzę, że to jest to, co zrobiłby w C.

 1
Author: chrish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 03:54:42

@compatibility_alias będzie w stanie rozwiązać konflikty przestrzeni nazw klas, np.

@compatibility_alias NewAliasClass OriginalClass;

Jednakże nie rozwiąże to żadnego z enum, typedefów ani kolizji przestrzeni nazw protokołu . Co więcej, nie gra dobrze z @class przednimi declami oryginalnej klasy. Ponieważ większość frameworków będzie dostarczana z tymi nieklasowymi rzeczami, takimi jak typedefs, prawdopodobnie nie będziesz w stanie rozwiązać problemu przestrzeni nazw tylko przy compatibility_alias.

Spojrzałem na podobny problem do Twój , ale miałem dostęp do source i budowałem frameworki. Najlepszym rozwiązaniem, jakie znalazłem, było użycie @compatibility_alias warunkowo z # defines do obsługi enums/typedefs/protocols / etc. Możesz to zrobić warunkowo na jednostce kompilacji dla danego nagłówka, aby zminimalizować ryzyko rozszerzenia rzeczy w innym kolidującym frameworku.

 1
Author: Michael Chinen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-26 19:04:26

Prefiksowanie plików jest najprostszym rozwiązaniem, jakie znam. Cocoadev ma stronę przestrzeni nazw, która jest wysiłkiem społeczności, aby uniknąć kolizji przestrzeni nazw. Zapraszam do dodawania własnych do tej listy, wierzę, że to jest to, co jest.

Http://www.cocoadev.com/index.pl?ChooseYourOwnPrefix

 0
Author: Ryan Townshend,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-07 14:14:04

Jeśli masz kolizję, sugeruję, abyś się dobrze zastanowił nad tym, jak możesz refaktorować jedną z frameworków ze swojej aplikacji. Kolizja sugeruje, że te dwie rzeczy robią podobne rzeczy i prawdopodobnie możesz obejść się używając dodatkowego frameworka po prostu przez refaktoryzację aplikacji. Nie tylko rozwiąże to problem z przestrzenią nazw, ale także sprawi, że Twój kod będzie bardziej wytrzymały, łatwiejszy w utrzymaniu i bardziej wydajny.

Nad bardziej technicznym rozwiązaniem, gdybym był w twoja pozycja to będzie mój wybór.

 0
Author: Allyn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-05 21:20:00

Jeśli kolizja jest tylko na poziomie łącza statycznego, możesz wybrać bibliotekę używaną do rozwiązywania symboli:

cc foo.o -ldog bar.o -lcat

Jeśli foo.o i bar.o odwołują się do symbolu rat, to libdog rozwiąże foo.o's rat i libcat rozwiąże bar.o'S rat.

 0
Author: wcochran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-20 03:27:45

Tak sobie pomyślałem.. nie przetestowany lub sprawdzony i może być sposób na znak, ale w czy rozważałeś napisanie adaptera do klasy używasz z prostszych frameworków.. a przynajmniej ich interfejsy?

Jeśli napisałbyś wrapper wokół prostszych frameworków (lub tego, do którego interfejsów masz dostęp najmniej), nie byłoby możliwe skompilowanie tego wrappera do biblioteki. Ponieważ Biblioteka jest wstępnie skompilowana i tylko jej nagłówki muszą być dystrybuowane, być skutecznie ukrywanie podstawowych framework i byłoby wolne, aby połączyć go z drugim framework z clashing.

Doceniam oczywiście, że prawdopodobnie będą chwile, kiedy będziesz musiał używać klas z obu frameworków w tym samym czasie, jednak możesz zapewnić fabryki dla dalszych adapterów klas tego frameworku. Z tyłu tego punktu myślę, że potrzebujesz trochę refaktoryzacji, aby wyodrębnić interfejsy, których używasz z obu frameworków, które powinny zapewnić ładny punkt wyjścia do budowy owijarki.

Możesz rozbudowywać bibliotekę w miarę potrzeb i kiedy będziesz potrzebował dodatkowych funkcji z zawiniętej biblioteki i po prostu przekompilować, gdy się zmieni.

Ponownie, w żaden sposób udowodnione, ale czułem się jak dodanie perspektywy. mam nadzieję, że to pomoże:)

 0
Author: mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-31 16:01:52

Jeśli masz dwie struktury o tej samej nazwie funkcji, możesz spróbować je dynamicznie załadować. To nieeleganckie, ale możliwe. Jak to zrobić z klasami Objective-C, Nie wiem. Domyślam się, że klasa NSBundle będzie miała metody, które załadują określoną klasę.

 -1
Author: MaddTheSane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-18 00:54:49