Introspection (surtout connaà®tre la classe) via KVC ?
AliGator
Membre, Modérateur
Bonjour,
Je cherche à connaà®tre la classe d'une variable d'un objet, à priori par KVC.
Je m'explique, soit la classe suivante :
Seulement, avant de remplir, j'aimerais éventuellement convertir mes données dans le type attendu. Par exemple si je vois que c'est de type NSDate, je convertis ma NSString "2009-11-09 09:00:00" venant de mon XML en NSDate avant de faire [tt]setValue:forKey:[/tt].
Mieux encore, si ce n'est pas un NSObject mais un NSInteger, l'encapsuler dans un NSNumber pour pouvoir appeler [tt]setValue:forKey:[/tt], etc.
Je ne pense pas que ça puisse être fait directement par KVC, il faut peut-être passer par les méthodes du Runtime (toutes mes variables à setter par [tt]setValue:forKey:[/tt] ont des @property associées, pour pouvoir les lire via le getter ou via la syntaxe pointée, donc peut-être à vérifier là -dedans ?), si vous avez des suggestions...
Je cherche à connaà®tre la classe d'une variable d'un objet, à priori par KVC.
Je m'explique, soit la classe suivante :
@interface Dummy : NSObject<br />{<br /> NSString* aString;<br /> NSDate* aDate;<br /> NSInteger anInteger;<br />}<br />@property(nonatomic, retain) NSString* aString;<br />@property(nonatomic, retain) NSDate* aDate;<br />@property(nonatomic, assign) NSInteger anInteger;<br />@end
J'utilise massivement le KVC (car j'en ai plein des classes comme ça et je les remplis depuis du XML, avec un code générique qui utilise donc [tt]setValue:forKey:[/tt]).Seulement, avant de remplir, j'aimerais éventuellement convertir mes données dans le type attendu. Par exemple si je vois que c'est de type NSDate, je convertis ma NSString "2009-11-09 09:00:00" venant de mon XML en NSDate avant de faire [tt]setValue:forKey:[/tt].
Mieux encore, si ce n'est pas un NSObject mais un NSInteger, l'encapsuler dans un NSNumber pour pouvoir appeler [tt]setValue:forKey:[/tt], etc.
Je ne pense pas que ça puisse être fait directement par KVC, il faut peut-être passer par les méthodes du Runtime (toutes mes variables à setter par [tt]setValue:forKey:[/tt] ont des @property associées, pour pouvoir les lire via le getter ou via la syntaxe pointée, donc peut-être à vérifier là -dedans ?), si vous avez des suggestions...
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Au moins tu retrouveras la classe concernée en KVC ...
J'arrive à récupérer le type, et à agir en conséquence.
Seul problème : l'héritage. En fait dans le cas d'un NSObject, l'encodedType est du genre [tt]@NSString[/tt].
J'avais commencé en faisant un isEqualToString pour ventiler selon le cas NSString, NSDate, NSNumber, ... et convertir ma chaà®ne en ce type d'objet avec la méthode appropriée selon les cas.
Le soucis, c'est que si j'ai une variable d'instance de type NSMutableString, je détecte pas que c'est une NSString !
J'ai bien pensé donc récupérer la Class correspondante, avec [tt]Class cls = objc_getClass(className)[/tt], et ça me retourne bien une Class, mais avec le même nom que celui passé, ce qui est problématique pour les Class Clusters. par exemple ça me retourne une classe "NSString" et pas "NSCFString".
Du coup (enfin je pense que c'est pour ça), si je lui demande ensuite [tt][cls isKindOfClass:[NSString class]][/tt] il me répond... NO !
La seule solution que j'ai trouvé c'est de créer une instance de la classe pour tester son type (car init peut renvoyer un type différent de la classe elle-même)... mais je sais pas si c'est top... une autre suggestion ?
kind of: 1
member of: 0
Du coup que ce soit une NSMutableString ou une NSString, ça nous dit YES quand on demande si c'est une sous-classe de NSMutableString... Et qu'on ait une MyMutableString ou une NSString, impossible de faire la différence...
Bon je m'en suis accomodé, puisque de toute façon je m'en fiche, c'est juste pour savoir si c'est une chaà®ne, et dans ce cas je fais [tt][cls stringWithString:machaine];[/tt] comme ça ça crée une NSString ou une NSMutableString ou une MyMutableString dans tous les cas. C'est juste que je trouvais la réponse YES étrange, faisant croire qu'une NSString est une NSMutableString... Mais j'imagine que c'est à cause du fait que c'est un class cluster.
[tt]+ (BOOL) respondsToSelector:@selector(stringWithCapacity[/tt] non ?
J'ai pas testé mais à priori tu aurais un résultat fiable non ?
Le même qu'avec [tt]isMemberOfClass[/tt] sur l'instance ?
[EDIT] mais si! cette approche pourrait résoudre le problème d'éventuelles sous-classe avec la méthode de Classe de NSObject [tt]+ (BOOL)isSubclassOfClass:(Class)aClass[/tt]
Pas besoin d'instancier la classe pour tester.
En fait c'est normal qu'un [tt]isKingOfClass[/tt] envoyé à une classe ne marche pas dans ton code puisque c'est une méthode d'instance non ?
[EDIT2]Â Bon ça résout le côté inélégant d'instancier une classe juste pour la tester, après j'ai pas testé et il est possible que la réponse soit aussi surprenant qu'avec [tt]isKindOfClass[/tt] appliqué à une instance ... ?
Pour l'égalité pure des classes, je ne veux pas non plus, car je veux que ça réponde oui si cls est une sous-classe de celle que je teste, par exemple si l'utilisateur a créé une sous-classe perso.
S'il a créé une classe MyMutDico, sous-classe de NSMutableDictionary, je veux pouvoir détecter que c'est un NSMutableDictionary, qui est lui-même un NSDictionary.
Sinon, dans la doc de isKindOfClass, ils disent que si le receiver est une Class, ça marche aussi
Mais en effet, j'avais loupé [tt]+isSubclassOfClass:[/tt], merci Par contre faut espérer que ça fasse pas des misères avec les class clusters, puisque si ma className, obtenue par extraction depuis property_getAttributes ou autre @encode(), est NSString, ça ne revient pas au même de faire le test sur la classe ou sur l'instance (puisque [tt]-[NSString init][/tt] renvoie non pas une NSString mais une NSCFString)
Bref, pas simple tout ça !
en effet la doc dit:
Sauf que dans le cas de Class Cluster elle est encore abstraite puisque non instanciée.
Du reste la doc dit aussi:
Alors que pour [tt]isSubclassOfClass[/tt] il n'est fait mention d'aucune "special consideration" ce qui laisse espérer un comportement plus transparent...
Là , le hors de question est un peu brutal.
Tu parlais depuis 2 posts de tes difficultés à différencier une NSString d'une NSMutableString (et leurs éventuelles sous classes respectives).
T'as pas ici à tester toutes les classes imaginables mais juste un seul est unique appel à [tt]respondsToSelector[/tt] auquel la classe comme les sous-classes éventuelles répondront convenablement.
Je t'accorde bien volontiers que c'est moins beau, mais c'est un "work around" efficace et plus élégant en s'adressant directement à la classe que dans ta construction instanciant un "dummy".
Bien sur si tu veux aussi diférencier aussi toutes les autres class Cluster mutable/non mutable ça fait une ligne de code chaque fois pour chaque classe cluster ... mais c'est pas le bout du monde non plus.
De toutes façons [tt]isKindOfClass[/tt] devrait être la bonne solution élégante ... j'attends tes tests avec impatience.Â
[EDIT] re-formulation plus claire (en italique)
Bon ceci dit, je met en demi-pause cette partie du projet car il faut que j'avance sur d'autres points plus urgents (et plus "visuels" pour le client) et pas que je m'attarde trop sur ce point. J'y reviendrais peut-être plus tard, mais malheureusement là je suis plutôt obligé de faire du "quick & dirty" (ce que je déteste, étant perfectionnister ;D) qu'une architecture propre et générique, mais bon, client difficile, faut bien s'adapter
Je compatis, je connais ça dans ma branche aussi, mais je suis obligé de reconnaitre qu'il faut réellement parfois épargner "au client" le temps qui serait nécessaire à faire de l'élégant si on peut lui fournir rapidement une solution efficace (et solide dans le temps tout de même)
Y'a un moment où la beauté de l'art de nos métiers doit s'effacer devant les besoins réels de "nos clients", voire même un moment où ce perfectionisme peut lui nuire.
Mais le truc, c'est qu'en fait comme ça que je faisait au début, mais ça ne résout pas le pb des class clusters, qui m'imposent d'instancier pour connaà®tre la "vraie" classe.
Par exemple si j'ai une variable d'instance de type NSString, le typeEncoding retourné sera [tt]@NSString...[/tt], dont je peux extraire le nom de la classe "NSString". Mais d'après ce nom de classe je ne peux pas avoir la vraie classe "NSCFString"
D'ailleurs je fais toujours comme ça (et pour rester KVC-Compliant je teste même tous les cas, si ma clé est définie comme une @property, si non et si accessInstanceVariablesDirectly est à YES pour la classe, alors je teste les Ivars, et récupère leur typeencoding, etc...)
N'empêche qu'une fois que j'ai le TypeEncoding, j'ai toujours le mm problème, si c'est une class cluster, impossible de connaà®tre la classe sous-jacente utilisée sans instancier...