unrecognized selector sent to instance...
pinus
Membre
Bonjour,
Je me bats depuis ce matin à essayer de comprendre pourquoi je me cogne cette erreur. J'appelle une fonction d'un sigleton :
La première fois que je l'appelle, aucun souci. La seconde fois (et toutes les fois qui suivent), je reçois le message console suivant :
*** -[DBController GetColumnListforTable:]: unrecognized selector sent to instance 0x141eb0
Pourtant, en pas à pas dans le debugeur, j'ai bien mon instance (unique) de DBController chargée, avec tout ce qu'il faut dedans. Pourquoi diable ne reconnaà®t-il plus GetColumnListforTable ???
Des pistes où chercher ? je ne sais plus où aller ;-)
D'avance merci.
Pinus.
Je me bats depuis ce matin à essayer de comprendre pourquoi je me cogne cette erreur. J'appelle une fonction d'un sigleton :
GetColumnListforTable:
La première fois que je l'appelle, aucun souci. La seconde fois (et toutes les fois qui suivent), je reçois le message console suivant :
*** -[DBController GetColumnListforTable:]: unrecognized selector sent to instance 0x141eb0
Pourtant, en pas à pas dans le debugeur, j'ai bien mon instance (unique) de DBController chargée, avec tout ce qu'il faut dedans. Pourquoi diable ne reconnaà®t-il plus GetColumnListforTable ???
Des pistes où chercher ? je ne sais plus où aller ;-)
D'avance merci.
Pinus.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
0x141eb0 c'est bien l'adresse de DBController ?
ça me renvoie bien "DBController" :
2009-01-02 19:01:15.024 REAL Client[5768:10b] myDBController is not nil
2009-01-02 19:01:15.024 REAL Client[5768:10b] <DBController: 0x141eb0>
2009-01-02 19:01:15.025 REAL Client[5768:10b] classe: DBController
2009-01-02 19:01:15.028 REAL Client[5768:10b] *** -[DBController GetColumnListforTable:]: unrecognized selector sent to instance 0x141eb0
Il faut donc que ta méthode soit définie avec
+(....) GetColumnListforTable:....
ou alors si le singleton est myDBController, il faut faire [myDBController GetColumnListforTable:...]
avec une méthode
-(....) GetColumnListforTable:....
Pour voir l'utilisation des singletons , voir
file:///Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.CoreReference.docset/Contents/Resources/Documents/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_1.html
J'ai justement écrit mon Singleton par rapport à cette doc (peut-être me suis-je gouré cela dit).
J'appelle bien la méthode de mon Singleton comme suit :
Auparavant, je récupère une référence à cette instance de la manière suivante :
J'ai peut-être (probablement !) mal pigé la doc. Mais ce qui m'intrigue, c'est que ça marche nickel la 1ere fois...
Pinus.
Erreur
Revoies la définition de ta classe DBController, et de ce que renvoie la méthode +(DBController) sharedDBController.
Je comprends bien ce que tu m'expliques. ça sonne assez "logique". Pourtant, je ne renvoie dans "sharedDBController" rien d'autre qu'une instance "shared" de la classe DBController. Du reste, lors du premier appel à la méthode "GetColumnListforTable:", je n'ai aucune erreur. Ce sont les appels suivant qui retournent cette erreur.
Bien entendu que j'ai dû faire une ânerie. C'est évident. Mais après une journée pleine passé dessus, je n'arrive pas à voir où je me plante.
voici mes méthodes de classe (si ça te parle) :
En tout état de cause, merci pour ton aide, elle m'est précieuse !
Pinus.
As-tu codé aussi release et autorelease comme indiqué afin d'éviter une destruction du singleton ?
(id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return UINT_MAX; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
Oui le problème peut être que myDBController (=shared)Â est créé par [super allocWithZone:zone], et qu'ainsi son champ isa n'est pas celui d'un DBController.
Ben non, c'est le init qui précise la classe , voir post suivant.
% gcc pgm.m -o pgm -framework Foundation
% pgm
2009-01-02 20:35:46.544 pgm[2408:10b] SingletonClass
%
[/quote]
Non, ça ne joue pas. Mais merci quand même ! ;-)
Pinus.
Oui, j'ai implémenté ces méthodes également.
Pinus.
J'y ai bien pensé, car cette idée de "désallocation" me paraà®t la plus probable (puisque tout fonctionne correctement lors de la première exécution), mais je ne vois pas comment c'est possible : en exécutant pas à pas dans le debuger, j'ai bien TOUJOURS mon instance valide où je peux voir que mes variables sont valides elles aussi, et alimentées. Je veux bien que cette instance pointe au diable, mais si c'était le cas, je ne verrais pas mes 2 NSArray instanciés et initialisées correctement.
J'ai l'impression que je me suis mis dans un sacré pétrin ;-)
On dirait que c'est l'appel en (lui-même) à la fonction, qui fait tout partir en vrille car si j'exécute cette ligne :
..elle est exécutée sans problème..
et la ligne qui suit immédiatement :
arrête tout et me log l'erreur "unrecognized selector sent..."
..et ce, à partir de la seconde exécution.
J'avoue que j'en perds mon Latin...
PS : beurk les noms de tes classes...
GetColumnListforTable <-> GetColumnListForTable ??
Autrement quel est le prototype de cette méthode ?
Le projet ne pourra pas être exécuté puisqu'il se connecte à une base de données sur mon réseau.
Mais en bref :
ma classe DBController est le singleton en question, à partir de laquelle je tape dans ma base de données.
Dans mon appli, je fais ça (à partir d'une autre classe) :
La 1ere fois que c'est exécuté, aucun souci.
dès la seconde fois, je me cogne l'erreur en question, bien que myDBController est parfaitement instancié et que j'ai accès aux variables d'instance (par ex : [myDBController.dbTableFields removeAllObjects];).
En même temps, je ne cherche pas à faire un concours de style. Par ailleurs, les questions de goût... c'est toujours très subjectif. Mais je te promets de faire un effort une fois que j'aurais "réglé" mon problème ;-)
En tout éta de cause, merci.
Ces trois possibilités sont tout aussi improbables les unes que les autres...
Et le débogueur ne sera pas capable de t'aider.
Les tests à faire sont plutôt simples... La première tu effaces le nom et tu fais un copier/coller du nom dans le .m, pour la deuxième, tu fais un NSLog(@%p, myDBController); avant l'exécution de la méthode et tu vérifies si l'adresse ne change pas. Et dernière vérification, tu la fais avec la méthode respondsToSelector: tu devrais aussi utiliser les fonctions disponibles dans le runtime directement.
La seule solution que je vois pour l'instant, c'est créer un nouveau projet et copier tes sources d'un projet à l'autre, des fois, voire souvent, ça corrige les bogues inexpliqués...
PS: Sinon ce n'est pas une question de style, c'est plutôt une question de conventions, mais bon peu importe.
Non , Objective.C permet d'envoyer des messages à nil .
Le fait qu'il ne râle pas sur [myDBController.dbTableFields removeAllObjects] ne prouve pas l'existence de myDBController.
===========================================================
#import <Foundation/Foundation.h>
@interface ClassA:NSObject {
NSMutableArray * table;
}
@property(nonatomic,retain) NSMutableArray * table;
-(void)print;
@end
@implementation ClassA
@synthesize table;
-(void)print{
NSLog(@%@",table);
}
@end
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
ClassA * a=[[ClassA alloc] init];
a.table=[NSMutableArray arrayWithObjects:@un,@deux,nil];
[a print];
a=nil;
[a.table removeAllObjects];
NSLog(@%@",a.table);
[pool drain];
return 0;
}
% gcc pgm2.m -o pgm -framework Foundation
% pgm
2009-01-02 23:56:58.815 pgm[647:10b] (
un,
deux
)
2009-01-02 23:56:58.817 pgm[647:10b] (null)
%
Tu peux aussi essayer un
[EDIT]Bon je viens de relire psychoh13, c'est plus ou moins ce qu'il te conseille de faire aussi comme test pour l'un de ses 3 cas en fait ^^[/EDIT]
Oui, complètement. Mais le simple fait que, exécutant :
... myDBController.dbTableFields passe de 124 objets à 0, tend à m'inspirer sur le fait que mon instance existe bel et bien. Non ?
2009-01-03 00:41:47.700 REAL Client[7339:813] 1er appel = <DBController: 0x141d70>
2009-01-03 00:41:47.710 REAL Client[7339:813] 2e appel = <DBController: 0x141d70>
2009-01-03 00:41:47.713 REAL Client[7339:813] 3e appel = <DBController: 0x141d70>
2009-01-03 00:41:47.713 REAL Client[7339:813] 4e appel = <DBController: 0x141d70>
merci.
Les 3 ne m'apprenent rien de plus :-( Tout à l'air normal, si ce n'est que cette méthode n'est plsu reconnue dès le second appel.
Oui, et bien je crois qu'il n'y a plus que ça. J'imagine que je dois faire une belle connerie quelque part, mais là , ça devient nébuleux ;-)
Autant pour moi alors ;-)
2009-01-03 00:48:35.989 REAL Client[7383:813] 1er appel = <DBController: 0x141d70> (DBController)
2009-01-03 00:48:35.991 REAL Client[7383:813] 2e appel = <DBController: 0x141d70> (DBController)
Merci.
Avec ta base de données qui doit être conséquente, tu n'as pas de débordement incontrôlé ?
Par exemple, avec le projet Cocoa ci-dessous, l'exécution plante au bout du troisième appel sur le bouton, et gdb me dit que c'est dans une méthode graphique que cela plante ...
#import "AppController.h"
#import "SingletonClass.h"
static char *str;
@implementation AppController
-(void) awakeFromNib {
SingletonClass* shared=[SingletonClass sharedObject];
shared.table=[NSArray arrayWithObjects:@un,@deux,nil];Â
str=malloc(100);
}
-(IBAction) buttonAction:(id)sender {
SingletonClass* shared=[SingletonClass sharedObject];
NSLog([shared className]);
[shared print];
str=memset(str,'o',110)+110;
}
@end
ou encore :
Avec ça tu auras des tests fiables...
Ensuite, tu vas répliquer que tu t'occuperas du style après, mais ça, ça fait partie des bonnes habitudes à prendre... Il vaut mieux éviter de permettre à du code externe, même si c'est le tien, de pouvoir modifier le contenu d'un objet, sans passer par l'objet lui-même...
Je pense en particulier à ta propriété qui permet de récupérer un NSMutableArray et à qui tu envoies le message removeAllObjects.
Non seulement c'est anti-programmation objet, mais c'est aussi plutôt dangereux, il vaut mieux une méthode intermédiaire qui te permette de filtre les différentes actions que tu fais sur tel objet, ce sera déjà beaucoup facile à maintenir et les comportements seront concentrés dans une seule classe.
Alors que permettre aux autres objets d'accéder et de modifier le contenu des variables d'instances sans passer par l'objet c'est plus aléatoire...
Je dis ça par expérience, je me suis évité plein d'emmerdes à écrire des méthodes proxy pour gérer les ivars de mes objets, et pour récupérer les données des objets modifiables comme des NSMutableDictionary ou NSMutableArray, même si le code ne dépasse pas ma juridiction, je retourne des copies...
ça limite les comportements inattendus, et ça évite de se perdre quand on écrit plusieurs milliers de lignes de code sur plusieurs séances...
OK, je vais tester avec ça. Merci. Non mais loin de moi l'idée de contredire Aligator (d'autant que c'est Philippe49 qui causait), "j'dis juste" (comme dirait les Deschiens) qu'en debug, je vois mon instance et que les données qu'elle contient sont celles qui sont censées y être. Maintenant, en effet, je ne suis certain de rien...
Aucune propriété de ma classe n'est modifié de l'extérieur. En fait, j'ai ajouté cette ligne pour des raisons de test: cela fait 2 jours que j'essaie de trouver d'où vient cette blague. Donc j'ai essayé plein de trucs. Donc je respectais bien l'encapsulation. Cela dit, il s'agit de propriétés synthétisées. Et la syntaxe monInstance.mavar n'est rien d'autre qu'un accesseur (mavar/setMavar). Cela dit, cette ligne n'était là que pour des raisons de test.
J'en prends note. Merci bien.
Pinus.
Les résultats sont très intéressant (voir pour moi : hallucinant):
-1ère exécution :
2009-01-03 11:35:18.476 REAL Client[7729:813] populateColums for :personnes
2009-01-03 11:35:26.473 REAL Client[7729:813] logX: 0x141d70 <
NSLog(@logX: %p, myDBController);
2009-01-03 11:35:27.391 REAL Client[7729:813] 0x144ab0 <
NSLog(@%p, class_getInstanceMethod([myDBController class], @selector(GetColumnListForTable));
2009-01-03 11:35:28.206 REAL Client[7729:813] DBController <
NSLog(@%s, object_getClassName(myDBController));
2009-01-03 11:35:31.613 REAL Client[7729:813] executing query : PRAGMA table_info(personnes)
2009-01-03 11:35:31.687 REAL Client[7729:813] getting 124 rows
-2eme exécution :
2009-01-03 11:35:36.908 REAL Client[7729:813] populateColums for :tels_pro
2009-01-03 11:35:39.532 REAL Client[7729:813] logX: 0x141d70 <
NSLog(@logX: %p, myDBController);
2009-01-03 11:35:39.532 REAL Client[7729:813] 0x0 <
NSLog(@%p, class_getInstanceMethod([myDBController class], @selector(GetColumnListForTable));
2009-01-03 11:35:39.533 REAL Client[7729:813] DBController <
NSLog(@%s, object_getClassName(myDBController));
2009-01-03 11:35:39.533 REAL Client[7729:813] *** -[DBController GetColumnListForTable:]: unrecognized selector sent to instance 0x141d70
2009-01-03 11:35:39.534 REAL Client[7729:813] *** -[DBController GetColumnListForTable:]: unrecognized selector sent to instance 0x141d70
Il semble que j'ai bien toujours mon instance mais que celle-ci, à la seconde exec, ne soit plus capable de gérer la méthode "GetColumnListForTable:". Moi, en tant que débutant, ça me calme....
Pinus...
(mettre des breakpoints sur myDBController=[DBController sharedController], et utiliser une option de menu contextuel)