# [Cocoa]Problème d'évènements.



## Askerat (14 Décembre 2008)

Bonjour, 

je suis nouveau sur le forum, je l'ai découvert en cherchant solution à mon sujet, pour me présenter rapidement, je suis en DUT informatique, et par avant j'ai pas mal pratiqué le Java/C++/php. J'ai commencé l'objective C que récemment, je commence à bien intégrer la logique du langage. Seulement je suis confronté à un comportement très étrange, j'espère qu'on saura me dire ce qui cloche.

Les fait :


				 			 			 				 			 				 					 					Dans une même classe héritée de NSWindow j'ai trois messages :
-(void) keyDownNSEvent*)event {
    [super event];
    [self autreMessage"unString"];
}

-(void) autreMessageNSString*)str{
    //Utilise le string envoyé.
    NSLog(str); // s'affiche correctement dans tous les cas.
    self->monString = [self->monString appendString:str]; //Et pourtant, ne s'ajoute pas si str vient de keyDown.
}

-(void) dernierMessage{
    [self autreMessage"unString"];
}
Que keyDown soit appellé, ou que j'appelle moi-même dernierMessage, NSLog(str); dans autreMessage m'affiche bien unString dans les deux cas, sauf que si je l'envoie depuis dernierMessage, autreMessage se comporte correctement, et depuis keyDown non. En gros autreMessage fait un appendString sur une propriétée de classe. J'ai fait le test en statique dans les deux cas biensur, sans utiliser le retour de l'objet NSEvent de keyDown. J'ai tout tenté, des stringWithString: & co. Pas moyen d'ajouter le NSString str à mon autre NSString, si str provient de keyDown. Je me retrouve toujours avec une chaine qui vaut nil. D'ou celà peut-il venir ? J'ai tenté le code hors de mon projet avec une seule classe MyWindow qui reprend l'exemple.


----------



## tatouille (14 Décembre 2008)

cretin de bug de post....


----------



## tatouille (14 Décembre 2008)

myString is Mutable sure



```
- (void)appendString:([URL="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/doc/c_ref/NSString"]NSString[/URL] *)aString

[self.myString appendString:[str retain]];
```
 " je commence à bien intégrer la logique du langage"
oui bof et puis la doc bof et le pointeur -> bof

"j'ai pas mal pratiqué le Java/C++/php"
c'est a dire au moins 5 ans sur des projets key?


----------



## Askerat (14 Décembre 2008)

Voici le message que j'utilise :

- (NSString *)stringByAppendingStringNSString *)_aString.

J'avais pas le source sous les yeux lorsque j'ai rédigé le message. Mais visiblement il se passe quelque chose de bizarre dans keyDown, puisque tout fonctionne bien si ma fonction est appellée ( de la meme maniere avec le meme parametre ) d'une autre fonction que keyDown.
_


----------



## tatouille (14 Décembre 2008)

un perform selector sur le main thread :rateau: allez hop


----------



## Askerat (14 Décembre 2008)

De manière plus explicite ça donne quoi ?


----------



## Askerat (14 Décembre 2008)

J'ai tout de même du mal à comprendre de quelle manière le message keyDown peut altérer le comportement d'un message qu'il envoie. Car mon message, envoyé de la même manière, l'instruction est la même, je le répète, car c'est bien ce qui me gêne le plus dans l'histoire, ne fonctionne pas lorsque cet envoi est fait depuis keyDown. Dans tout autre contexte ça fonctionne. 

J'ai par exemple essayé d'envoyer ce message via une action sur un bouton, et là ça marche. Surtout que dans tous les cas, ( keyDown, action bouton, ou statique ( appel dans init par exemple )) le message (autreMessage) m'affiche bien sur la sortie standart la bonne valeur de ma variable, puis lorsqu'il l'envoie à un autre message, je perds systématiquement la valeur, si la provenance du NSString vient de keyDown.


----------



## tatouille (15 Décembre 2008)

http://developer.apple.com/document...ew/HandlingKeyEvents/chapter_6_section_3.html


----------



## Céroce (15 Décembre 2008)

```
-(void) keyDown:(NSEvent*)event {
    [super event];
    [self autreMessage:@"unString"];
}
```

NSWindow n'a pas de méthode "event".



```
-(void) autreMessage:(NSString*)str
{
    //Utilise le string envoyé.
    NSLog(str); // s'affiche correctement dans tous les cas.
    self->monString = [self->monString appendString:str]; //Et pourtant, ne s'ajoute pas si str vient de keyDown.
}
```

self->monString: mais où es-tu aller chercher ça ? 

Moi j'écrirais:

```
NSString* newString = [monString stringByAppendingString:str];
[monString release];
monString = [newString retain];
```

Comme le dit Tatouille (à sa façon), il vaut mieux utiliser NSMutableString, dans ce cas là, c'est plus simple.


Franchement, ton code est du bricolage, il faut que tu apprennes les bases du langage ObjC, parce qu'il est clair que tu ne maîtrises pas ce que tu fais. Il ne faut pas que tu le prennes mal.

Inscris-toi ici.

Achète ce bouquin.


----------



## Askerat (15 Décembre 2008)

Ouais j'appelle bien keyDown, mais je vois pas où est le bricolage, puisque stringByAppendingString: retourne bien un nouveau NSString, je l'affecte donc naturellement. La méthode fonctionne très bien si l'appel n'est pas fait depuis keyDown, je le répète.


----------



## Céroce (15 Décembre 2008)

1) Supprime l'appel à
[super event];
et le message s'affichera certainement.

2) Après que la ligne

```
monString = [monString stringByAppendingString:str];
```
est executée
Que devient l'ancien objet pointé par monString ?
Jusqu'à quand l'objet monString va-t-il rester en mémoire ?


----------



## Askerat (15 Décembre 2008)

Bon j'ai déjà un peu tout tenté, j'ai ajouté un message pour voir justement jusqu'à quand existait ma chaine, ça nous donne donc :

-(void) keyDown: (NSEvent*)event {
        //[super event];
    [self autreMessage"unString"];
}

-(void) autreMessage: (NSString*)str{
         //Utilise le string envoyé.
    NSLog(str); // s'affiche correctement dans tous les cas.
    [self encoreUnMessage:str];
    NSLog(str); // S'affiche correctement dans tous les cas, donc str existe bien au sein de autreMessage, mais pas au sein de encoreUnMessage !

        NSString *tmp = [self->monString stringByAppendingString:str];
    [self->monString autorelease];
    self->monString = [tmp retain];
}

-(void) encoreUnMessageNSString*) str{
    NSLog(str); // Ne s'affiche pas lorsque l'origine de str est keyDown, s'affiche dans tous les autres cas.
}

-(void) dernierMessage{
        [self autreMessage"unString"];
}



Si entre temps personne trouve solution au problème, je crée une petite archive qui souligne le problème au boulot, seulement j'ai que acces au protocole http, je pourrai donc répondre ici, mais je ne pourrai le mettre en ligne que vers 22h. 

edit : Elle sera sous la forme d'un projet Xcode avec une simple classe fille de NSWindow qui aura un attribut et un bouton, afin de souligner différents appels du message qui nous cause tant de soucis.


----------



## Céroce (15 Décembre 2008)

J'ai repris ton code, j'ai juste modifié les méthodes de messages ainsi:


```
-(void) encoreUnMessage:(NSString*) str{
	NSLog(@"encoreUnMessage:%@", str);
}
```

et j'obtiens ça:


```
008-12-15 16:58:15.854 WindowResponder[1506:10b] autreMessage:unString
2008-12-15 16:58:15.863 WindowResponder[1506:10b] encoreUnMessage:unString
2008-12-15 16:58:15.867 WindowResponder[1506:10b] autreMessage:unString
```

Bref, ça marche.


----------



## Askerat (15 Décembre 2008)

Je suis au travail, je recrée la situation et je reposte.


----------



## Askerat (15 Décembre 2008)

Effectivement, j'ai reproduit la meme chose que toi, ça m'affiche la même chose, cependant, tente d'afficher sur la sortie standart le contenu de monString, tu verras qu'il ne s'affiche pas, car monString vaut toujours nil, hors, dans mon programme initial, lorsque la chaine vient d'une autre action, la concaténation se fait correctement.


----------



## Askerat (15 Décembre 2008)

Je pense avoir trouvé mon erreur. Juste une question, comment peut-on surcharger le constructeur de NSWindow ? Car visiblement init ne suffit pas. Et les initWithXXX meme en appellant [super initWithXXX] ne me rendent pas une fenetre complète.


----------



## Askerat (15 Décembre 2008)

Le problème vient bien d'un problème d'initialisation de la variable membre. Je met le projet en ligne à l'adresse suivante : https://pipit.u-strasbg.fr/~3boeglina/calto.tar. J'ai un peu bidouillé pour arriver à un résultat à peu prés convenable, en appuyant sur les boutons à la souris tout fonctionne, par contre ça bug complètement avec le clavier.


----------



## tatouille (15 Décembre 2008)

http://developer.apple.com/document...asses/nsobject_Class/Reference/Reference.html

*performSelectorOnMainThread*



http://developer.apple.com/document...NSNotification_Class/Reference/Reference.html

http://developer.apple.com/document...ficationCenter_Class/Reference/Reference.html

*NSNotificationCenter Class Reference*


*addObserver:selector:name object:
*
et vire moi ces pointeurs -> merci pour la signature de l'objet t'es assez lucky que ca marche oui des int ...

un peu de lecture 

http://code.google.com/p/archimade/source/browse/trunk/Classes/ArchimadeApplicationController.m


----------



## Céroce (16 Décembre 2008)

initWithContentRect:styleMask:backing:defer:
est l'initialiseur désigné de NSWindow. C'est donc lui qui est appelé.


Tes questions sont basiques. Il est normal que tu ne saches pas comment faire, c'est pourquoi tu dois lire le livre que je t'ai conseillé.


----------



## Askerat (16 Décembre 2008)

Je l'ai pourtant bien surchargé ce constructeur, mais effectivement, au début ma classe Calculator n'héritait pas de NSWindow, et j'avais une instance contenue dans mon Controller, du coup, j'initialise mon Calculator et dans mon NSApplication et dans mon Controller, ce qui fait 2NSWindow pour une fenetre effective, le soucis vient sans doute de là, j'aurais du revoir plus en profondeur l'organisation de mes objets en voulant ajouter la gestion clavier.

Mais bon, je trouve un peu domage qu'on veuille éviter de faire faire de l'héritage aux utilisateurs sans proposer de solution convenable pour les évènements. J'ai beaucoup travaillé avec Swing, donc j'ai un peu vite tendance à spécialiser les classes disponibles. Là je suis entrain de reconstruire l'application.

Pour le livre, je l'ai commandé il y a deux jours sur amazon, il devrait arriver d'ici ce week end au plus tard à prioris.

Pour ce qui est des pointeurs, dis moi tatouille quel est le problème ? Je sors d'un an de dev en Java, et tous les exemples que je vois initialisent des objets avec des pointeurs. En java tout étant référence, le comportement doit être similaire, et il faut bien faire attention à ce que l'on met dans les méthodes copy ( clone en java ) de nos objets. A la différence pres que là on doit gérer la mémoire. Je pensais que le NSAutoreleasePool  gérait ça, mais visiblement on doit le gérer de manière plus suivie que ce qu'il parait au premier abord, je vais donc regarder un peu plus par là dans les prochains temps.


----------



## Céroce (16 Décembre 2008)

Tout sera beaucoup plus clair quand tu auras lu le bouquin. 
Par exemple, pourquoi pour intercepter les événements, on hérite de NSView et pas de NSWindow. Tu verras que les autorelease pools ne sont pas des ramasse-miettes (la gestion mémoire est expliquée au début du livre).

Tu verras aussi à quel point Swing est inférieur à Cocoa, bien qu'il soit plus accessible.


----------



## Askerat (16 Décembre 2008)

Et si on hérite de NSView, comment on met en pratique notre sous classe ? ^^ ( le livre ne sera probablement pas arrivé avant vendredi ).

edit: Peut-on voir les notifications de Cocoa comme les signaux / flots de Qt ? L'utilitée est un peu identique non ?


----------



## tatouille (16 Décembre 2008)

[[self myvar] message];
[self.myvar message];

obj->member 
my struct {

}

ici tu pointes sur les membres des structs
c'est comme une var member en C++ tu ne pointes pas dessus


----------



## Céroce (16 Décembre 2008)

Askerat a dit:


> Et si on hérite de NSView, comment on met en pratique notre sous classe ? ^^ ( le livre ne sera probablement pas arrivé avant vendredi ).



Il va falloir être patient, ça ne s'explique malheureusement pas sur un coin de forum.
En pratique, on supplante bien la méthode -[NSView keyDown], mais tu as une notion de "focus"  c'est à dire la vue actuelle qui reçoit les événements  qui est subtile.

Je ne connais pas Qt , mais j'imagine que ça correspond plutôt au système des targets/actions.
Les notifications s'utilisent plutôt dans des cas bien particuliers, par exemple, obliger l'utilisateur à ne saisir que des caractères minuscules dans un champ de texte. Ce n'est qu'un exemple.


----------



## tatouille (16 Décembre 2008)

Céroce a dit:


> Il va falloir être patient, ça ne s'explique malheureusement pas sur un coin de forum.
> En pratique, on supplante bien la méthode -[NSView keyDown], mais tu as une notion de "focus" &#8212; c'est à dire la vue actuelle qui reçoit les événements &#8212; qui est subtile.
> 
> Je ne connais pas Qt , mais j'imagine que ça correspond plutôt au système des targets/actions.
> Les notifications s'utilisent plutôt dans des cas bien particuliers, par exemple, obliger l'utilisateur à ne saisir que des caractères minuscules dans un champ de texte. Ce n'est qu'un exemple.



je ne suis pas d'accord les notifications s'utilisent dans tout key value change model
un event envoie des notifications pour avertir tout object dependant de lui que quelque choses vient de se passer, swing n'est que base sur ca comme tous les framework ou on a besoin de communiquer entre differents objects

en cocoa il faut en faire un usage extensif surtout dans des applis multi-threaded c'est la solution geniale
pour pas se taper des pointeurs sur parent.parent.parent et de plus ca permet de communiquer entre des threads 
etranges, par ailleurs si tu trifouilles cocoa a l'interieur tu peux voir que les inges d'Apple ne se privent pas
si tu colles iterate le spool du defaultcenter tu pourras etre etonne de la queue

pour un exemple plus large

http://www.freedesktop.org/wiki/Software/dbus



```
-(void) keyDown:(NSEvent*)event{
    if(self->m_displayMem == nil)
        self->m_displayMem = [[Memory alloc] init];
    if(self->m_outMem == nil)
        self->m_outMem = [[Memory alloc] init];
    if(self->m_proc == nil)
        self->m_proc = [[Processor alloc] init];
    
    NSString *tmp = [event characters];
    [tmp retain];
    if([tmp isEqualToString:@"0"])
        [self didNumberButtonPushed:tmp];
    else if([tmp isEqualToString:@"1"])
        [self didNumberButtonPushed:tmp];
    else if([tmp isEqualToString:@"2"])
        [self didNumberButtonPushed:tmp];
    else if([tmp isEqualToString:@"3"])
        [self didNumberButtonPushed:tmp];
    else if([tmp isEqualToString:@"4"])
        [self didNumberButtonPushed:tmp];
    else if([tmp isEqualToString:@"5"])
        [self didNumberButtonPushed:tmp];
    else if([tmp isEqualToString:@"6"])
        [self didNumberButtonPushed:tmp];
    else if([tmp isEqualToString:@"7"])
        [self didNumberButtonPushed:tmp];
    else if([tmp isEqualToString:@"8"])
        [self didNumberButtonPushed:tmp];
    else if([tmp isEqualToString:@"9"])
        [self didNumberButtonPushed:tmp];
    else if([tmp isEqualToString:@"+"])
        [self didOperatorButtonPushed:tmp];
    else if([tmp isEqualToString:@"-"])
        [self didOperatorButtonPushed:tmp];
    else if([tmp isEqualToString:@"/"])
        [self didOperatorButtonPushed:tmp];
    else if([tmp isEqualToString:@"*"])
        [self didOperatorButtonPushed:tmp];
    else if([tmp isEqualToString:@"="])
        [self didEqualButtonPushed];
}
```
ici ca me semble necessaire...


----------



## Askerat (16 Décembre 2008)

Je suis sur le point de finaliser une refonte totale de cette petite calculatrice basique. Dans celle ci j'ai ( hormis les classes Processor et Memory ) une classe Controller et une sous classe de NSWindow, dans les keyDown, j'envoie des notifications à mon controller, qui ensuite sait quoi faire. Donc au final, un clique ou un keyDown finit au même message, ce qui parait plutot cohérent, si jamais vous vous sentez l'âme de relire ça, j'ai fait un peu plus attention à la mémoire, notament sur mes getters/setters.


----------



## Céroce (17 Décembre 2008)

tatouille a dit:


> je ne suis pas d'accord les notifications s'utilisent dans tout key value change model



Tu donnes un sens très large à "Notification". Évidemment,  les systèmes de Target/actions et Key-Value observing _notifient_ également l'observateur d'un changement.

Mais dans la terminologie Cocoa, les notifications correspondent à quelque chose de précis, qui dépend de la classe NSNotification. Je pense que c'est ce à quoi Askerat faisait référence.


----------



## Askerat (17 Décembre 2008)

Oui tout à fait. Et, encore une question, dans la documentation, à quel endroit peut on savoir quel constructeur est appellé par le NIB ? Car là j'ai le même problème que j'avais pour le NSWindow, j'ai sous classé NSOpenGLView, pour gérer l'initialisation etc, j'ai redéfinit le constructeur, mais celui ci n'est jamais visité, donc ça doit être un constructeur parent de NSOpenGLView qui est appellé j'imagine. Mais dans la documentation, à aucun endroit est indiqué quel constructeur est appellé. 

Autre question, de manière généralle dans ce cas ci, vaut mieux utiliser awakeFromNib, ou bien le constructeur ( message init quoi ) pour initialiser notre instance ? Le constructeur ça me parait plus propre, et plus logique, je ne vois pas awakeFromNib pour cet usage ci.


----------



## tatouille (17 Décembre 2008)

pour une 
initWithNibName

pour une window

initWithWindowNibName

mais tu ne devrais/dois pas les surcharger quand tu choisis d'utiliser un xib
c'est pour reduire le code par pour merdifier
utilises les delegates

- (void)windowDidLoad
- (void)awakeFromNib

c'est fou ce que t'es curieux

http://code.google.com/p/archimade/source/browse/trunk/Classes/ArchimadeProgressWindowController.m


----------



## Askerat (17 Décembre 2008)

Je tente d'apprendre à utiliser le framework donc forcément, je me pose des questions ^^.


----------



## Céroce (18 Décembre 2008)

Askerat a dit:


> Mais dans la documentation, à aucun endroit est indiqué quel constructeur est appellé.



La méthode d'init appelée est celle pour laquelle la doc dit "This method is the designated initializer for the [classe] class". Il faut le savoir.
Donc, a priori, ce serait plutôt -[NSView initWithFrame:] qui faudrait surcharger.
La doc dit que spécifier le pixelFormat se fait dans les réglages de la NSOpenGLView sous Interface Builder.


----------

