2009-07-26 9 views
1

Je suis relativement nouveau à Cocoa/ObjC. Est-ce que quelqu'un pourrait m'aider à changer mon code pour utiliser les appels réseau asynchrones? il semble actuellement comme ceci (exemple fictif):Callbacks dans ObjC + Cocoa

// Networker.m 
-(AttackResult*)attack:(Charactor*)target { 
    // prepare attack information to be sent to server 
    ServerData *data = ...; 
    id resultData = [self sendToServer:data]; 
    // extract and return the result of the attack as an AttackResult 
} 

-(MoveResult*)moveTo:(NSPoint*)point { 
    // prepare move information to be sent to server 
    ServerData *data = ...; 
    id resultData = [self sendToServer:data]; 
    // extract and return the result of the movement as a MoveResult 
} 


-(ServerData*)sendToServer:(ServerData*)data { 
    // json encoding, etc 
    [NSURLConnection sendSynchronousRequest:request ...]; // (A) 
    // json decoding 
    // extract and return result of the action or request 
} 

Notez que pour chaque action (attaque, déplacer, etc.), la classe Networker a une logique pour convertir et de ServerData. Il est inacceptable de s'attendre à ce que les autres classes de mon code traitent ce ServerData.

Je dois faire de la ligne A un appel asynchrone. Il semble que la manière correcte de procéder est d'utiliser [NSURLConnection connectionWithRequest: ... delegate: ...] en implémentant un callback pour effectuer le post-traitement. C'est la seule manière que je peux penser pour le faire:

//Networker.m 
-(void)attack:(Charactor*)target delegate:(id)delegate { 
    // prepare attack information to be sent to server 
    ServerData *data = ...; 
    self._currRequestType = @"ATTACK"; 
    self._currRequestDelegate = delegate; 
    [self sendToServer:data]; 
    // extract and return the result of the attack 
} 

-(void)moveTo:(NSPoint*)point delegate:(id)delegate { 
    // prepare move information to be sent to server 
    ServerData *data = ...; 
    self._currRequestType = @"MOVE"; 
    self._currRequestDelegate = delegate; 
    [self sendToServer:data]; 
    // extract and return the result of the movement 
} 


-(void)sendToServer:(ServerData*)data { 
    // json encoding, etc 
    [NSURLConnection connectionWithRequest:...delegate:self] // (A) 
} 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 
    //json decoding, etc 
    switch(self._currRequestType) { 
     case @"ATTACK": {...} // extract and return the result of the attack in a callback 
     case @"MOVE": {...} // extract and return the result of the move in a callback 
    } 
} 

Cependant, ceci est très moche et pas thread-safe. Quelle est la bonne façon de faire cela?

Merci,

+0

Pouvez-vous me dire à quoi sert un exemple fictif de code pour ANYBODY? Je dois dire que c'est à peu près la même chose que de prendre la voiture de votre voisin chez le mécanicien parce qu'il y a quelque chose qui ne va pas chez vous. – Sneakyness

+1

Il est plus facile d'utiliser une simple analogie de jeu pour illustrer mes problèmes de conception que d'expliquer la fonctionnalité quelque peu compliquée de l'application que je développe. – tba

Répondre

1

Une option est d'avoir une instance d'objet par commande/demande; En bonus, vous pouvez utiliser des sous-classes pour que la gestion du type soit polymorphe plutôt que basée sur une grosse instruction switch. Faire un objet de commande de base avec une méthode initWithDelegate: (puis avoir des initiales spécialisées dans les sous-classes correspondant aux commandes qui ont besoin d'arguments) et des méthodes pour la plomberie d'envoi/réception de base. Chaque sous-classe peut implémenter un handleResponse: ou similaire qui est appelé depuis votre baseclass connectionDidFinishLoading :.

Si vous voulez cacher cela aux clients de ce service, vos méthodes attack:, moveTo :, etc. peuvent masquer l'instanciation de ces objets, de sorte que les clients interagiraient avec la même API.

+0

C'est une bonne idée, mais il semble que beaucoup de code supplémentaire. – tba

+0

Si vous faites un essai, vous trouverez probablement que ce n'est pas autant de code supplémentaire que vous le pensez - tout ce que vous devez faire est de répartir votre code existant sur quelques classes. Mais oui, parfois de bons designs prennent un peu plus de code. La compréhensibilité et la maintenabilité devraient être des considérations beaucoup plus importantes que le nombre brut de lignes. – smorgan

+0

Ce fut une douleur à mettre en œuvre, mais au moins maintenant, je peux faire des choses comme garder une trace de la demande actuelle et arrêter/réessayer. – tba