NSProxy et warnings

AliGatorAliGator Membre, Modérateur
mai 2010 modifié dans Objective-C, Swift, C, C++ #1
Bonjour à  tous,

Pour implémenter des appels à  un service JSON-RPC, je souhaite faire une classe qui dérive de NSProxy, de sorte de pouvoir appeler un peu n'importe quelle méthode dessus et que ça transforme l'appel de message en une requête JSON-RPC vers la méthode du WebService.

Exemple d'usage voulu :
id serv = [JSONRPCServiceProxy serviceProxyWithUrl:[NSURL URLWithString:@&quot;http://www.raboof.com/Projects/Jayrock/Demo.ashx&quot;]];<br />// appel &quot;virtuel&quot; qui va être transformé en l&#39;appel à  la méthode getRowArray du WebService<br />[serv getRowArray]; // pourtant JSONRPCServiceProxy n&#39;implémente pas cette méthode mais va la forwarder<br />// appel &quot;virtuel&quot; qui va être transformé en l&#39;appel à  la méthode echo du WebService<br />[serv echo:[NSArray arrayWithObject:@&quot;Hello&quot;]]; // pourtant JSONRPCServiceProxy n&#39;implémente pas cette méthode mais va la forwarder


Donc j'ai implémenté ce qu'il faut comme méthodes dans ma sous-classe JSONRPCServiceProxy de NSProxy, en particulier forwardInvocation: et methodSignatureForSelector:, qui permettent quand j'appelle une méthode inconnue (genre getRowArray) sur mon objet, que ça transforme cet appel Objective-C en un appel au WebService :
- (void)forwardInvocation:(NSInvocation *)anInvocation<br />{<br />	NSString* methodName = [[NSString stringWithUTF8String:sel_getName([anInvocation selector])]<br />							stringByReplacingOccurrencesOfString:@&quot;:&quot; withString:@&quot;&quot;];<br />	<br />	id params = nil;<br />	int nbArgs = [[anInvocation methodSignature] numberOfArguments];<br />	if (nbArgs&gt;2) [anInvocation getArgument:&amp;params atIndex:2];<br />	if (!params || [params isKindOfClass:[NSArray class]])<br />	{<br />		JSONRPCHandler* d = [_service callMethodWithName:methodName parameters:params];<br />		[anInvocation setReturnValue:&amp;d];<br />	} else {<br />		[super forwardInvocation:anInvocation];<br />	}<br />}


Et ça marche nickel, ça fait bien mon appel distant à  la méthode du WebService quand j'appelle la méthode Objective-C :)


Mon problème alors ? Bah c'est que du coup j'ai des warnings comme quoi les méthodes n'existent pas.
Ca parait un peu normal, sauf que je pensais qu'en utilisant une sous-classe de NSProxy, où justement c'est forcément typiquement le cas, bah j'aurais pu éviter d'avoir des warnings de partout ! Parce que bon, c'est fait pour, dans la doc Apple c'est bien dit que c'est tout à  fait idéal pour les appels à  des services distants ("Subclasses of NSProxy can be used to implement transparent distributed messaging (for example, NSDistantObject) [...]")... mais si c'est pour se taper des warnings...


• Si je type mon proxy en "id" ([tt]id serviceProxy = [[[JSONRPCServiceProxy alloc] init...] autorelease][/tt]), il me met un warning genre "No -xxx method found" quand j'appelle la méthode xxx.
• Si je type mon proxy en JSONRPCServiceProxy, le warning est "JSONRPCServiceProxy may not respond to -xxx"


Vous avez une idée pour supprimer ces warnings envoyés à  des NSProxy, sans pour autant idéalement désactiver le warning dans le projet au risque de ne plus avoir de warnings de ce genre quand ils sont cette fois justifiés...

Réponses

  • yoannyoann Membre
    11:12 modifié #2
    Tu as un début de piste ici http://borkware.com/rants/agentm/elegant-delegation/ en jouant avec NSMethodSignature pour générer la signature des méthodes inconnue. Je n'ai pas testé si ça fonctionne mais c'est vrai que c'est un peut con que NSProxy ne soit pas prévu pour éviter les warning si c'est techniquement possible...
  • AliGatorAliGator Membre, Modérateur
    11:12 modifié #3
    Merci pour cette piste... mais toute cette partie là , je l'ai déjà  codée !
    En effet, j'ai une class JSONRPCService, qui a entre autres une méthode [tt]- (JSONRPCHandler*)callMethodWithName:(NSString *)methodName parameters:(NSArray*)params;[/tt] qui va faire l'appel de la méthode au WebService.
    Et j'ai mon proxy donc, qui est codé ainsi :
    @interface JSONRPCServiceProxy : NSProxy<br />{<br />	JSONRPCService* _service;<br />}<br />@property(nonatomic, assign) JSONRPCService* service;<br />-(id)initWithService:(JSONRPCService*)service;<br />@end
    
    et
    @implementation JSONRPCServiceProxy<br />@synthesize service = _service;<br />- (id) initWithService:(JSONRPCService*)service<br />{<br />	_service = service;<br />	return self;<br />}<br />- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {<br />	NSMethodSignature *ret = nil;<br />	if (! (ret = [_service methodSignatureForSelector:aSelector])) { <br />		NSString* mName = [NSString stringWithUTF8String:sel_getName(aSelector)];<br />		NSArray* parts = [mName componentsSeparatedByString:@&quot;:&quot;];<br />		int nArgs = [parts count] - 1;<br />		if (nArgs==0) {<br />			// -(id self,SEL _cmd, NSString* methodName)<br />			ret = [NSMethodSignature signatureWithObjCTypes:&quot;@:@&quot;];<br />		} else if (nArgs==1) {<br />			// -(id self,SEL _cmd, NSString* methodName , NSArray* args)<br />			ret = [NSMethodSignature signatureWithObjCTypes:&quot;@:@@&quot;];<br />		} else {<br />			return nil;<br />		}<br />	}<br />	return ret;<br />}<br /><br />- (void)forwardInvocation:(NSInvocation *)anInvocation<br />{<br />	NSString* methodName = [[NSString stringWithUTF8String:sel_getName([anInvocation selector])]<br />							stringByReplacingOccurrencesOfString:@&quot;:&quot; withString:@&quot;&quot;];<br />	<br />	id params = nil;<br />	int nbArgs = [[anInvocation methodSignature] numberOfArguments];<br />	if (nbArgs&gt;2) [anInvocation getArgument:&amp;params atIndex:2];<br />	if (!params || [params isKindOfClass:[NSArray class]])<br />	{<br />		JSONRPCHandler* d = [_service callMethodWithName:methodName parameters:params];<br />		[anInvocation setReturnValue:&amp;d];<br />	} else {<br />		[super forwardInvocation:anInvocation];<br />	}<br />}<br /><br />@end
    
    Donc tout marche bien comme je le disais plus haut... le mécanisme d'implémentation de mon NSProxy qui forward mes messages à  mon WebService marche parfaitement, et me permet d'utiliser [tt][monProxy getUsers][/tt] ou [tt][getRecordsForUsers:arrayOfUsers][/tt] au lieu de [tt][monService callMethodWithName:@getUsers parameters:nil][/tt] ou [tt][monService callmethodWithName:@getRecordsForUser parameters:arrayOfUsers][/tt].

    Sauf justement qu'il y a ces fameux warnings quand je fais les appels sur monProxy et que j'aimerai éliminer ! Car vu que ce sont des messages envoyés à  un NSProxy, et que NSProxy est justement fait pour forwarder les invocations, le warning ne me parait pas très significatif... contrairement à  quand le message est envoyé à  un NSObject !
  • yoannyoann Membre
    11:12 modifié #4
    Au temps pour moi, j'ai mal traduit un des paragraphe, il parlait d'un warning a faire sauter mais pas ceux la...

    Un Perrier pour me faire pardonner ?  :p
  • zoczoc Membre
    11:12 modifié #5
    dans 1272807974:

    Vous avez une idée pour supprimer ces warnings envoyés à  des NSProxy, sans pour autant idéalement désactiver le warning dans le projet au risque de ne plus avoir de warnings de ce genre quand ils sont cette fois justifiés...

    A part déclarer un @protocol contenant toutes les méthodes exposées par le service JSON (avec l'attribut @optional) et l'utiliser, ou déclarer un protocole informel (catégorie de NSOBject), il n'y a pas vraiment de solution (ou alors désactiver spécifiquement ce warning dans les options de compilation, mais il peut être utile en cas de faute de frappe...).

  • AliGatorAliGator Membre, Modérateur
    mai 2010 modifié #6
    Ok merci zoc, c'est un peu ce que je craignais...
    Et oui, je ne souhaitais pas désactiver tous les warnings, justement parce que ce genre de warning est utile pour d'autres cas.
    Je pensais juste que, dans le cas particulier d'un NSProxy, qui est donc justement fait, par définition, pour rerouter les messages (et donc ne répond pas lui-même aux messages qu'on lui envoie), et donc que pour lui ces warnings là  n'ont pas trop de sens, on pouvait faire qqch, sans altérer le reste...


    Déclarer un @protocol (voire un protocole informel) c'est une idée, mais en fait ça revient à  la 2e étape de mon implémentation : aujourd'hui j'ai fini mon framework générique pour appeler n'importe quel service JSON-RPC, et maintenant quand je vais vouloir l'utiliser, je vais me créer de toute façon une classe spécifique qui va appeler le WebService dont j'ai besoin, et contenir des méthodes faisant interface avec mon WebService. Genre :
    -(JSONRPCServiceHandler*)echo:(NSString*)str {<br />&nbsp; [self callMethodWithNameAndParams:@&quot;echo&quot;,str,nil];<br />}<br /><br />// appel<br />[[service echo:@&quot;Hello&quot;]<br />&nbsp; setDelegate:self callback:@selector(echoCall: didRespond: error:)];<br /><br />// callback<br />-(void)echoCall:(JSONRPCMethodCall*)mc didRespond:(NSString*)resp error:(NSError*)err<br />{ ... }
    
    Mais bon, mon but c'était qd mm d'avoir un truc global que je pourrais publier, sans warning, pour que les autres puissent le réutiliser dans un cas générique. Et donc pouvoir, au lieu d'utiliser "callMethodWithNameAndParams:...", d'utiliser :
    [service.proxy echo:@&quot;Hello&quot;,nil]
    
    sans avoir de warning...

    Aujourd'hui ça marche, mais avec warnings :-(
  • AliGatorAliGator Membre, Modérateur
    11:12 modifié #7
    [Mode modo]
    @Hiike : J'ai déplacé ton mesage ici car il n'était pas forcément dans le sujet le plus approprié ici. Merci.
    [/Mode modo]
Connectez-vous ou Inscrivez-vous pour répondre.