8

Questo è un problema che mi ha infastidito da un po 'di tempo. Sono ancora piuttosto nuovo con alcuni di questi modelli, quindi dovrai perdonarmi (e correggermi) se uso uno dei termini in modo errato.Come posso implementare il modello di localizzazione del servizio in Cocoa Touch su più progetti?

la mia metodologia

Ho creato un motore di gioco. Tutti gli oggetti nel mio motore di gioco utilizzano l'inversione del controllo per ottenere dipendenze. Queste dipendenze implementano tutti i protocolli e non sono mai accessibili direttamente nel progetto, se non durante la fase di bootstrap. Per ottenere questi oggetti, ho il concetto di un localizzatore di servizi. Il lavoro del localizzatore di servizi è quello di individuare un oggetto conforme ad un protocollo specifico e restituirlo. È molto simile a una fabbrica, ma dovrebbe gestire anche le dipendenze.

Per fornire i servizi al localizzatore di servizio, dispongo di quelli che chiamo specificatori di servizio. Il localizzatore di servizi conosce tutti gli specificatori del servizio nel progetto e, quando viene richiesto un oggetto, tenta di ottenere un'istanza di un oggetto conforme al protocollo fornito da ciascuno di essi. Questo oggetto viene quindi restituito al chiamante. La cosa interessante di questo set up è che lo specifier del servizio conosce anche un localizzatore di servizi, quindi se ha delle dipendenze, chiede semplicemente al localizzatore di servizi quelle specifiche dipendenze.

Per fare un esempio, ho un oggetto chiamato HighScoreManager. HighScoreManager implementa il protocollo PHighScoreManager. In qualsiasi momento se è richiesta un'istanza PHighScoreManager, esso può essere recuperato chiamando:

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

Così, inversione di controllo. Tuttavia, la maggior parte delle volte non è nemmeno necessario farlo, poiché la maggior parte delle classi si trova in un identificatore del servizio, se si richiede PHighScoreManager come dipendenza, quindi viene recuperato tramite il localizzatore di servizi. Quindi, ho un buon approccio piatto all'inversione del controllo.

mio problema

Perché voglio il codice dal mio motore di gioco da condividere, l'ho compilato come una libreria statica. Funziona benissimo per tutto il resto, ma sembra essere un po 'complicato con il localizzatore di servizi. Il problema è che alcuni servizi cambiano da un gioco all'altro. Nel mio esempio precedente, un punteggio in una partita potrebbe essere un tempo e in un altro potrebbe essere punti. Pertanto, HighScoreManager dipende da un'istanza di PHighScoreCreator, che indica come creare un oggetto PScore.

Al fine di fornire PHighScoreCreator a HighScoreManager, ho bisogno di avere un identificatore di servizio per il mio gioco. L'unico modo in cui potevo pensare di realizzare questo era usare la versione di cacao delle riflessioni. Dopo aver scavato, ho scoperto che le lezioni erano disponibili attraverso NSBundle, ma sembra che non ci sia modo di ottenere il pacchetto attuale. Quindi, se voglio essere in grado di cercare i miei specificatori di servizio, dovrei compilare la mia logica di gioco nel suo bundle, e poi fare in modo che il motore cerchi questo bundle e lo carichi. Per fare questo dovrei creare un terzo progetto per ospitare sia il codice motore che il pacchetto di logica di gioco, quando in realtà mi piacerebbe avere solo un progetto di gioco che utilizzava la libreria statica del motore.

la mia vera domanda

Così, dopo tutto questo, la mia domanda è

  1. Esiste un modo migliore per fare quello che sto cercando di realizzare in Cocoa Touch, o
  2. C'è un modo per scoprire classi che sono conformi al mio protocollo identificatore di servizio dal pacchetto principale?

Grazie per l'aiuto e il tempo per leggere la domanda.

-helixed

risposta

1

Dai un'occhiata alla:

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

Questi consentono di interagire a livello di programmazione con i vari pacchetti in fase di esecuzione. Una volta che hai un pacchetto con cui lavorare, ci sono un certo numero di strategie che potresti impiegare per trovare le classi specifiche che stai cercando. Ad esempio:

  1. Recupera l'identificatore del gruppo - questo sarà un NSString come @ "com.example.GameEngineClient".
  2. Trasformalo in un nome di classe Objective-C legale eliminando tutto prima dell'ultimo punto, oppure sostituendo tutti i punti con trattini bassi o altro, quindi aggiungendo un nome di protocollo predefinito. Il tuo protocollo dall'alto, ad esempio, potrebbe risultare in una stringa come @ "GameEngineClient_PHighScoreManager".
  3. Ottieni la classe designata del bundle per il protocollo utilizzando NSClassFromString().

Ora è possibile creare un'istanza della classe fornita dall'autore del pacchetto, che implementa qualsiasi protocollo specificato.

Il runtime Objective-C è una cosa meravigliosa!

+0

Ho provato a seguire questo percorso e il problema è che non puoi sapere in anticipo quali nomi di classe saranno inclusi nel pacchetto. Pertanto, non è possibile eseguire iterazioni su tutte le classi nel pacchetto. – LandonSchropp

+0

Ho modificato la risposta per fornire alcuni suggerimenti su cosa fare dopo avere un'istanza NSBundle. Spero che questo ti aiuti. :-) –

+0

Ho la sensazione che questo potrebbe non essere consentito in un'app per iPhone, ma potrei sbagliarmi. Sono abbastanza sicuro che Apple non ti voglia di caricare le lezioni in fase di esecuzione su iPhone. Ma grande domanda. –

1

Sembra che sia necessario utilizzare le funzioni di Objective-C runtime. Innanzitutto è possibile ottenere un elenco di tutte le classi disponibili tramite objc_getClassList. Quindi puoi scorrere tutte le classi e verificare se sono conformi al tuo protocollo con class_conformsToProtocol. Non si dovrebbero usare i messaggi +conformsToProtocol: qui, poiché nel runtime ci sono classi che non supportano questo selettore.

Problemi correlati