Hack super moche dans une catégorie
Bonjour à tous,
J'ai un petit problème spécifique qui arrive à la compilation et qui est vachement emmerdant, alors je prie pour qu'il y ait quelqu'un qui l'ait déjà eu et qui a réussit à le corriger sur ce forum.
Voilà mon problème vient d'une méthode définie dans une catégorie de la classe NSAffineTransform. La méthode permet juste de faire une rotation autour d'un point donné et non plus autour du point d'origine du repère.
Pour pouvoir faire cette méthode, je dois accéder puis modifier l'unique variable d'instance de cette classe qui est de type NSAffineTransformStruct. Le problème c'est que lorsque je tente d'y accéder, mon compilateur me renvoie une erreur de résolution de symbole :
Je précise que ma méthode se trouve dans un Framework Cocoa pour Leopard que je développe.
Donc, comme l'indique ce message d'erreur, je n'arrive pas à utiliser directement la variable d'instance alors qu'ils s'agit d'une catégorie (théoriquement j'ai donc accès à toutes les variables d'instance même les @private).
Le plus troublant c'est que plus tôt dans la journée, ce système marchait très bien, je n'avais rien modifié entre temps et tout à coup j'ai eu ce message.
Alors, temporairement j'ai recours à un hack super moche et pas vraiment pérenne :
Ce que je voudrais savoir, c'est si vous avez une solution à mon problème et qui me permettrait d'utiliser le comportement normal ?
Merci beaucoup.
J'ai un petit problème spécifique qui arrive à la compilation et qui est vachement emmerdant, alors je prie pour qu'il y ait quelqu'un qui l'ait déjà eu et qui a réussit à le corriger sur ce forum.
Voilà mon problème vient d'une méthode définie dans une catégorie de la classe NSAffineTransform. La méthode permet juste de faire une rotation autour d'un point donné et non plus autour du point d'origine du repère.
Pour pouvoir faire cette méthode, je dois accéder puis modifier l'unique variable d'instance de cette classe qui est de type NSAffineTransformStruct. Le problème c'est que lorsque je tente d'y accéder, mon compilateur me renvoie une erreur de résolution de symbole :
"_OBJC_IVAR_$_NSAffineTransform._transformStruct", referenced from:
-[NSAffineTransform(PSYRotationAdditions) rotateByRadians:withCenterPoint:] in NSAffineTransform_PSYAdditions.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Je précise que ma méthode se trouve dans un Framework Cocoa pour Leopard que je développe.
Donc, comme l'indique ce message d'erreur, je n'arrive pas à utiliser directement la variable d'instance alors qu'ils s'agit d'une catégorie (théoriquement j'ai donc accès à toutes les variables d'instance même les @private).
Le plus troublant c'est que plus tôt dans la journée, ce système marchait très bien, je n'avais rien modifié entre temps et tout à coup j'ai eu ce message.
Alors, temporairement j'ai recours à un hack super moche et pas vraiment pérenne :
NSAffineTransformStruct *transformStruct = (NSAffineTransformStruct*)((id)self + 1);
Ce que je voudrais savoir, c'est si vous avez une solution à mon problème et qui me permettrait d'utiliser le comportement normal ?
Merci beaucoup.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Sinon, certes c'est moins optimisé que de faire le calcul directement sur les éléments de la matrice, mais il suffit de faire l'application habituelle : Qui consiste à définir la transformation comme un déplacement pour amener le point (x,y) en (0,0), puis effectuer une rotation, puis ramener à l'endroit où cela était avant.
C'est aussi simple que d'utiliser [tt]transformStruct[/tt] et [tt]setTransformStruct[/tt] pour appliquer les calculs sur ces derniers : finalement ça revient au même, et je suis pas persuadé que ce soit moins optimisé de faire avec mon code que directement sur les valeurs de la matrice...
D'ailleurs, même s'il y a des chances que la représentation interne des NSAffineTransform soit cette transformStruct (quoique l'erreur que tu as est en fait sûrement justement dû au fait que ce n'est plus le cas ?), rien ne dit dans la doc que tu peux accéder directement à cette variable privée (autrement dit, rien ne garantit dans la doc que cette variable privée sera toujours accessible dans les futures versions d'OSX)... et de toute façon c'est une variable privée, donc t'es pas sensé connaà®tre son existence et te baser dessus... :P (et puis si elle est privée, y a-t-on vraiment accès dans les catégories, ou n'est-ce le cas qu'avec les protected ?)
-- Alors que [tt]transformStruct[/tt] et [tt]setTransformStruct:[/tt], eux, sont indiqués dans la doc et ont déjà un peu plus de chances d'être pérennes : même si la représentation interne des NSAffineTransform changent, ils pourront toujours modifier ces méthodes pour convertir la valeur interne en ces structures pour te donner accès directement aux données et vice-versa.
D'ailleurs, s'il n'y a pas de [tt]getTransformeStruct:[/tt] (qui donnerait directement accès à la valeur interne, d'après les conventions de nommage, valeur qui aurait donc directement un impact sur le NSAffineTransform si elle était modifiée, donc), c'est sans doute pas pour rien... est-ce vraiment sa représentation interne ? Ou juste une représentation alternative dans et depuis laquelle la classe sait convertir ses données ?
Non j'y accède directement.
Ouais justement je voudrais utiliser le moins possible de méthode.
Je dois avouer que je n'avais pas remarqué la présence de -setTransformStruct: :)beta: .
NSAffineTransform n'est pas un class-cluster, les variables d'instance sont donc parfaitement visible. Cette classe en possède une unique qui est de type NSAffineTransformStruct qui est explicitement @private. Et il est explicitement dit dans la documentation que les méthodes d'une catégorie sont équivalentes aux méthodes de la classe elle-même, tu peux parfaitement accéder à toutes les variables d'instance même les variables @private, et ça c'est explicitement dit dans la documentation. C'est pour ça qu'une catégorie a besoin de connaà®tre l'interface de la classe pour que le compilateur sache à quelles variables tu as le droit d'accéder.
Ah ça permets-moi d'en douter je veux bien que la représentation interne des NSString change du tout au tout entre deux version de Mac OS X, parce qu'il y a des centaines de manières de représenter une chaà®ne de caractères, mais l'utilisation de matrice pour les transformations reste tout de même le moyen le plus efficace de calculer les coordonnées de points.
Ah non, les noms commençant par "get" ne retournent pas l'adresse d'une variable d'instance, elles ne donnent en aucun cas un accès direct aux variables d'instance. Non, une méthode -getTransformStruct: aurait exactement le même objectif qu'une -transformStruct, c'est-à -dire récupérer la valeur de la variable d'instance. La différence entre les deux, c'est que -transformStruct retourne la valeur, alors que dans le cas de get tu fournis un espace mémoire (via une adresse) pour contenir la valeur demandée. Mais il n'a jamais été question d'accéder à l'espace mémoire.
En général, on utilise un get... explicite lorsqu'il y a plus d'une valeur à récupérer ce qui est impossible à faire avec un get implicite sans allocation de mémoire ce qui rendrait le get chiant à utiliser.
Non, vraiment, pour la catégorie, je suis censé avoir un accès total à la variable d'instance @private, d'ailleurs, ce n'est pas à la compilation à proprement parler qu'il y a l'erreur mais à la liaison. C'est le linker qui plante, et c'est ça qui me dérange le plus...
Sinon ton code ne marchera jamais... Parce que je les méthodes que tu utilises retournent void et pas id, donc tu peux pas faire des appels en cascade comme ça.
Par contre je reste persuadé qu'il est mieux d'utiliser transformStruct et setTransformStruct:
Evidemment, je suis d'accord avec toi, c'est la représentation matricielle qui restera toujours la mieux pour représenter une transformation affine, on est tous d'accord. Maintenant, pour des questions diverses et variées, entre autres le fait que ce soit une variable explicitement privée, même si tu peux donc y accéder dans ta catégorie, si tu peux éviter, pour moi, c'est mieux, car plus pérenne : Apple peut vouloir changer la représentation interne, ce qui ne veut pas dire que ce ne sera pas une représentation matricielle pour autant (par exemple avoir des variables d'instance séparées pour chaque élément de la matrice plutôt que la structure directement ?)
Je vois pas pourquoi ils feraient ça mais on sait jamais... et de toute façon ça vaut le coup de tenter avec transformStruct et setTransformStruct: pour voir si le problème persiste, non ?
Le problème ne vient pas de la compilation, et encore, si la méthode n'était pas définie avant, on aurait droit à un warning tout au plus.
Mais ici, mon problème vient du linker, et c'est ça qui m'emmerde. Il compile très bien, il voit que j'ai l'accès à la variable _transformStruct, d'ailleurs le compilateur transforme correctement le symbole, mais le linker n'est pas capable de retrouver ce symbole... D'où l'erreur.
Concernant les getters et autres méthodes, rien n'interdit de renvoyer un pointeur vers une donnée interne, mais en général, la valeur de retour est affublée de "const" afin d'éviter une modification de la part de l'utilisateur qui violerait le concept d'encapsulation.
Ex : "bytes" de NSData
[Edit] Les autres exemples trouvés dans "Foundation" :
"decodeBytesForKey" de NSCoder
"methodReturnType" et "getArgumentTypeAtIndex" de NSMethodSignature
"fileSystemRepresentationWithPath" de NSFileManager (qui apparemment, si je lis bien entre les lignes de la doc, crée un NSString avec le bon encodage, la bonne forme de composition... et renvoie sa représentation interne avant de le mettre dans l'AutoReleasePool)
"lossyCString", "cString", "UTF8String", "cStringUsingEncoding" de NSString (même remarque qu'au dessus)
"objCType" de NSDecimalNumber et de NSValue
"aeDesc" de NSAppleEventDescriptor
D'ailleurs, parfois la doc est très explicite là -dessus :
Tu as fait ta catégorie dans un framework ? Par ce que je pense que mon problème vient de là , enfin j'en suis même sûr parce que je fais le link correctement...
Sinon, bien sûr, si la doc dit explicitement qu'il s'agit d'une représentation interne, alors en effet il retourne un pointeur sur les données internes, mais si ce n'est pas dit explicitement alors ce n'est pas le cas.
D'ailleurs concernant certains exemples que tu donnes, je pense qu'il faut repréciser les choses.
Non ! J'essaierai dans un framework...
Quand ça renvoie un pointeur, deux cas :
- Il y a allocation
- Il n'y a pas allocation
S'il n'y a pas d'allocation, pas de lézard, c'est une représentation interne, pas d'autre choix.
Quand il y a allocation (et là ils le précisent dans la doc en général), il faut un mécanisme de gestion de la mémoire allouée ; et là y a pas 36 solutions...
- Soit il y a un appel spécifique pour libérer la mémoire (ce qui n'est pas le cas ici)
- Soit la mémoire est gérée par le NSAutoReleasePool à travers un autre objet
Mais dans ce cas, on reboucle sur le problème initial, donc ce pointeur vient de la représentation interne de cet autre objetÂ