8

To jest problem, który od pewnego czasu mnie dręczy. Wciąż jestem całkiem nowy z niektórymi z tych wzorców, więc będziesz musiał mi wybaczyć (i poprawić), jeśli użyjesz któregoś z nich niepoprawnie.Jak mogę wdrożyć wzorzec lokalizatora usług w programie Cocoa Touch w wielu projektach?

My Metodologia

Utworzyłem silnik gry. Wszystkie obiekty w moim silniku gry wykorzystują inwersję kontroli w celu uzyskania zależności. Wszystkie te zależności implementują protokoły i nigdy nie są dostępne bezpośrednio w projekcie, inne niż podczas fazy ładowania. Aby uzyskać te obiekty, mam koncepcję lokalizatora usług. Zadaniem lokalizatora usług jest zlokalizowanie obiektu zgodnego z określonym protokołem i zwrócenie go. To bardzo przypomina fabrykę, ale powinno również obsługiwać zależności.

Aby zapewnić usługi lokalizatorowi usług, mam to, co nazywam specyfikatorami usług. Lokalizator usługi zna wszystkie specyfikatory usług w projekcie, a gdy obiekt jest żądany, próbuje uzyskać instancję obiektu zgodną z dostarczonym protokołem z każdego z nich. Obiekt ten jest następnie zwracany do osoby dzwoniącej. To, co jest fajne w tej konfiguracji, to specyfikator usługi, który również zna się na lokalizatorze usług, więc jeśli ma jakieś zależności, po prostu pyta o lokalizator usług dla tych konkretnych zależności.

Aby podać przykład, mam obiekt o nazwie HighScoreManager. HighScoreManager implementuje protokół PHighScoreManager. W dowolnym momencie, jeśli wymagana jest instancją PHighScoreManager, może być pobrana przez wywołanie:

id<PHighScoreManager> highScoreManager = [ServiceLocator resolve: @protocol(PHighScoreManager)]; 

Zatem odwrócenie sterowania. Jednak w większości przypadków nie jest to konieczne, ponieważ większość klas znajduje się w specyfikatorze usług, jeśli jeden z wymaganych PHighScoreManager jest zależny, jest on pobierany za pośrednictwem lokalizatora usług. Tak więc mam ładne płaskie podejście do odwrócenia kontroli.

mój problem

Bo chcę kod z moim silniku gry mają być dzielone, mam to skompilowane jako statyczne biblioteki. Działa to niesamowicie dla wszystkich innych elementów, ale wydaje się, że jest trochę trudny z lokalizatorem usług. Problem polega na tym, że niektóre usługi zmieniają się w zależności od gry. W powyższym przykładzie wynik w jednej grze może być czasem, w innym może to być punkt. Tak więc, HighScoreManager zależy od instancji PHighScoreCreator, która mówi, jak utworzyć obiekt PScore.

W celu udostępnienia programu PHighScoreCreator na HighScoreManager, potrzebuję specyfikatora usługi dla mojej gry. Jedynym sposobem, w jaki mogłem o tym pomyśleć, było użycie wersji odbicia Cocoa. Po przekopaniu okazało się, że klasy można odkryć przez NSBundle, ale wydaje się, że nie ma sposobu na pobranie aktualnego pakietu. Tak więc, jeśli chcę móc wyszukiwać moje specyfikatory usług, będę musiała skompilować logikę gry do własnego pakietu, a następnie sprawię, że silnik przeszuka ten pakiet i załaduje go. W tym celu musiałbym stworzyć trzeci projekt zawierający zarówno kod silnika, jak i pakiet logiczny gry, podczas gdy w rzeczywistości chciałbym mieć projekt gry wykorzystujący statyczną bibliotekę silnika.

Moje Prawdziwe pytanie

Więc po tym wszystkim, że moje pytanie jest

  1. Czy istnieje lepszy sposób, aby robić to, co staram się osiągnąć w Cocoa Touch lub
  2. Czy istnieje sposób na wykrycie klas zgodnych z protokołem specyfikacji usług z głównego pakietu?

Dzięki za pomoc i poświęcenie czasu na przeczytanie pytania.

-helixed

Odpowiedz

1

Zobacz w:

  • + [NSBundle mainBundle];
  • + [NSBundle bundleForClass:];
  • + [NSBundle bundleWithIdentifier:];
  • + [NSBundle allBundles];
  • + [NSBundle allFrameworks];

Umożliwiają one programową interakcję z różnymi pakietami w czasie wykonywania. Gdy masz już pakiet do pracy, istnieje szereg strategii, które możesz zastosować, aby znaleźć konkretną klasę, której szukasz. Na przykład:

  1. Pobierz identyfikator pakietu - będzie to NSString podobny do "com.example.GameEngineClient".
  2. Przekształć w legalną nazwę klasy C, usuwając wszystko przed ostatnią kropką lub zastępując wszystkie kropki podkreśleniami lub cokolwiek, a następnie dołączając wcześniej zdefiniowaną nazwę protokołu. Na przykład Twój protokół z powyższego może spowodować napis taki jak "GameEngineClient_PHighScoreManager".
  3. Uzyskaj wyznaczoną klasę pakietu dla swojego protokołu za pomocą NSClassFromString().

Teraz można utworzyć instancję klasy udostępnioną przez autora pakietu, który implementuje dowolny określony protokół.

Środowisko wykonawcze Objective-C to piękna rzecz!

+0

Próbowałem iść tą ścieżką, a problem polega na tym, że nie możesz wiedzieć z wyprzedzeniem, jakie nazwy klas będą zawarte w pakiecie. Dlatego nie można wykonywać iteracji po wszystkich klasach w pakiecie. – LandonSchropp

+0

Edytowałem odpowiedź, aby podać kilka sugestii co dalej robić po wystąpieniu NSBundle. Mam nadzieję że to pomoże. :-) –

+0

Mam przeczucie, że może to nie być dozwolone w aplikacji na iPhone'a, ale mogę się mylić. Jestem całkiem pewny, że Apple nie chce, abyś ładował klasy w czasie wykonywania na telefonie iPhone. Ale świetne pytanie. –

1

Wygląda na to, że musisz korzystać z funkcji Objective-C runtime. Najpierw możesz uzyskać listę wszystkich dostępnych klas za pośrednictwem objc_getClassList. Następnie możesz powtórzyć wszystkie zajęcia i sprawdzić, czy odpowiadają one Twojemu protokołowi z class_conformsToProtocol. Nie powinieneś używać tutaj komunikatów +conformsToProtocol:, ponieważ w środowisku wykonawczym istnieją klasy, które nie obsługują tego selektora.

Powiązane problemy