Controller-Methode, die normalerweise von einem Button aufgerufen wird, händisch aufrufen

  • Controller-Methode, die normalerweise von einem Button aufgerufen wird, händisch aufrufen

    Moin! Ich hab mal wieder eine Cocoa-Anfängerfrage:

    Wie kann ich eine Controller-Methode, die normalerweise von einem Button aufgerufen wird, händisch aufrufen?

    Wir schreiben in der HAW gerade einen RPN-Taschenrechner in Java, der auch einfache Script-Dateien speichen und laden/ausführen können soll. Die Script-Dateien enthalten pro Zeile entweder eine Double-Zahl, oder einen Code für eine Operation, z.B. "enter", "x^2" usw.

    Hier hatten wir es so gemacht, daß der Actionlistener eines Buttons eine Methode von mehreren unseres Controllers aufruft und einen Integer-Wert mit dem Code (als Konstanten. 0-9 = Ziffer, DOT = Punkt usw.) der Funkion übergibt. Innerhalb der Methoden wird dann per switch-case genauer unterschieden. Beim Auswerten einer Script-Datei können wir direkt die Controller-Methoden aufrufen, da sie ja nur eine Integer-Konstante erwarten (jetzt würde ich es mit Enums machen).



    Ich hab nun angefangen, diesen Taschenrechner in Cocoa nachzuschreiben (mein erstes Coco-Programm). Ich wollte das im Prinzip genauso abfeiern, wie in der Java-Version:

    Ich hab mehrere Tastengruppen auf der GUI (Ziffernblock, Funktionen usw.) als Matrix. Jede dieser Button-Gruppen ruft bei Aktivierung eine Methode des Controllers auf (z.B. enterDigit, wenn der Ziffernblock aktiviert wurde). In den jeweiligen Methoden werte ich per switch-case den selectedTag vom sender aus.

    Das klappt auch alles wunderbar. Nur hab ich jetzt das Problem: Ich parse die Datei (per stringWithContentsOfFile gelesen) und schaue mir jede Zeile an (mit NSScanner). Mit parseDouble schaue ich, ob in der Zeile eine Zahl ist. Wenn ja, wird die Zeile (der String) Ziffer für Ziffer an den Controller (enterDigit) geschickt. Wenn es nicht klappt, schaue ich, welcher Befehl im String steht und rufe die entsprechende Controller-Methode (z.B. enterFunction) auf. (Ich schicke die Zahlen deshalb Ziffer für Ziffer, wie eine Eingabe über die Tastatur, da ich die schon funktionierende Logik nutzen wollte und mir das in diesem Fall vertretbar erscheint.)

    Nun endlich zum Problem: Wie rufe ich händisch die Controller-Methoden auf? Die Methoden erwarten ja ein (id)sender, den ich nach dem selectedTag abfragen kann. Mit der Doku liege ich noch ein bisschen auf Kriegsfuß, aber so langsam (wirklich nur langsam) steigt die Übersicht und man bekommt ein Gefühl für das Benennungsschema (was sich ja z.T. deutlich von dem von HJava unterscheidet).

    Oder hätte man das Problem hier vielleicht anders angehen sollen?

    Ich hoffe, es ist zu verstehen, was ich meine. Ich könnte auch Code posten, falls es mehr helfen sollte.
  • RE: Controller-Methode, die normalerweise von einem Button aufgerufen wird, händisch aufrufen

    Ich wäre das Problem in der Tat anders angegangen und hätte die Action-Schicht von der funktionallen getrennnt. Die Action-Methoden häztten dann nur durchgereicht. Aber gut, dass wirst du jetzt nicht mehr ändern können.

    Du fragst einfach in der Action-Methode ab, was du bekommen hast. Ich verstehe dich jetzt so, dass du eine Action für alle Buttons hast? Dann so:

    Quellcode

    1. - (IBAction)buttonPressed:(id)sender {
    2. int tag;
    3. if( [sender isKindOfClass:[NSNumber class]] ) {
    4. tag = [NSNumber intValue];
    5. } else if( [sender respondsToSelector:@selector( tag )] ) {
    6. tag = [sender tag];
    7. }
    8. }
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • RE: Controller-Methode, die normalerweise von einem Button aufgerufen wird, händisch aufrufen

    Original von Tom9811
    Ich wäre das Problem in der Tat anders angegangen und hätte die Action-Schicht von der funktionallen getrennnt. Die Action-Methoden häztten dann nur durchgereicht. Aber gut, dass wirst du jetzt nicht mehr ändern können.

    Ich wäre aber trotzdem an einem Ansatz interessiert. Vielleicht mache ich das dann ja so.


    Du fragst einfach in der Action-Methode ab, was du bekommen hast. Ich verstehe dich jetzt so, dass du eine Action für alle Buttons hast?

    Ja, so in etwa:

    Quellcode

    1. - (IBAction)enterFunction:(id)sender {
    2. double result;
    3. switch ([sender selectedTag]) {
    4. case XPOW2: result = pow([currentValue doubleValue], 2); break;
    5. ...
  • Original von WoSoft
    ich glaube, er meint so etwas:

    Quellcode

    1. [self performSelector:@selector(idDesButtons:)];

    Ich hab bisher, wenn man die GUI mitzählen will, 3 Klassen (RPNController, RPNProtocol) und einen Stack als Category zu NSMutableArray (um dem Array passende Stackmethoden zu verpassen. Wollte nur mal mit den Categories probieren). Hätte ich jetzt nicht ein Problem in meinem RPNProtocol (die erzeugt die händischen Controller-Aufrufe) zu wissen, welche Buttons die GUI hat? Dann wäre ich ja gezwunden die GUI an die RPNProtocoll-Klasse zu fesseln. Wie ich es bisher verstanden habe, soll doch gerade nur der Controller kontakt zur GUI haben, oder?


    PS: Den Taschenrechner werde ich mir heute abend auf der Arbeit ansehen.
  • RE: Controller-Methode, die normalerweise von einem Button aufgerufen wird, händisch aufrufen

    Dann mache es so, wie ich oben im Kot: Wenn der Sender ein NSNumber-Objekt ist, fragst du nicht selectedTag, sondern intValue ab.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Ach, den anderen Teil habe ich vergessen:

    Quellcode

    1. - (void)executeOperation:(int)opCode {
    2. switch( opcode ) {
    3. // Deine Auswahl
    4. }
    5. }
    6. - (IBAction)enterFunction:(id)sender
    7. {
    8. if( [sender respondsToSelector:@selector( selectedTag )] ) {
    9. [self executeOperation:[sender selectedTag]];
    10. }
    11. }
    12. // Hauptprogramm
    13. [self executeOperation:gelesenerOpCode];
    Alles anzeigen
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Ich wollte jetzt mal die erste Möglichkeit ausprobieren, die Tom genannt hat:

    Quellcode

    1. // in RPNController
    2. - (IBAction)enterNumber:(id)sender {
    3. int tag;
    4. if ([sender isKindOfClass:[NSNumber class]]) tag = [sender intValue];
    5. else if ([sender respondsToSelector:@selector(selectedTag)]) tag = [sender selectedTag];
    6. ...


    Nur wie rufe ich den programmatisch auf? Wenn ich schreibe

    Quellcode

    1. // in RPNProtocol. target zeigt auf ein RPNController-Objekt
    2. [target enterNumber:[NSNumber numberWithInt:bla]];

    dann bekomme ich den Debugger um die Ohren:

    Quellcode

    1. 2007-05-21 02:30:42.471 RPNCalc[712] *** -[NSController enterNumber:]: selector not recognized [self = 0x306f00]
    2. 2007-05-21 02:30:42.471 RPNCalc[712] *** Uncaught exception: <NSInvalidArgumentException> *** -[NSController enterNumber:]: selector not recognized [self = 0x306f00]

    Die Code-Vervollständigung von Xcode führt die Methode auf. Der Fehler scheint mir das übergebene Argument zu sein, oder? Wie formuliere ich den Aufruf korrekt?
  • Der Fehler scheint mir das übergebene Argument zu sein, oder?

    Nein, der Fehler scheint ein falsches 'target' zu sein.
    Es gehört zur Klasse 'NSController', und nicht zu 'RPNController'.
    I would be embarrassed if they did not spy on me.
  • RPNController ist abgeleitet von NSObject und stellt mein Controller/Model vom MVC dar. Das imho korrekt so (lasse mich aber gerne eines besseren belehren).

    Vielleicht ist es das Beste, wenn ich den Projektordner mal hochlade. Das ist mein erstes ObjC/Cocoa-Programm. Wenn da jemand sonst noch irgendwelche Anmerkungen/Verbesserungsvorschläge hätte, wäre ich sehr dankbar.

    Ich habe die main.m verändert. Die GUI wird aktuell nicht gestartet gestartet. Ich teste hier gerade die RPNProocol-Klasse. Auf dem Desktop muß noch eine Textdatei "stringwriter.txt" angelegt werden. Für diese Testzwecke reicht es, wenn dadrin nur eine (double) Zahl drin steckt.
  • AAAAAAAARGH!!!!!!! Wieso sieht man selber sowas nicht?!? ;(

    Danke.

    Dieser Part funktioniert nun. Allerdings kommt das nächste Problem. In meiner "enterNumber"-Methode frage ich den sender, ob er von einer NSNumber abstammt, was scheinbar verneint wird. Wieso ist das jetzt ein NSCFNumber und kein NSNumber mehr?!? Sollte, wenn schon NSNumber irgendwo (wo?) zu einem NSCFNumber gemacht wird, dieses Objekt nicht wenigstens die gleichen Methoden haben?

    Quellcode

    1. -[NSCFNumber selectedTag]: selector not recognized

    Meine Prüfung, ob das nun ein NSNumber ist schlägt fehl. Dann wird sender darauf getestet, ob sie selectedTag versteht. Das scheint sie zu verstehen, dann aber, wenn diese Methode auch wirklich angewendet wird, doch nicht. Wieso?

    PS: Das ich den Punkt und das Vorzeichen noch nicht abhandel weiß ich, aber bei einer reinen positiven Integer-Zahl sollte es zumindest schonmal klappen (was es aber derzeit nicht tut).
  • Den Tag bekomme ich ja korrekt heraus, das ist nicht das Problem.

    Das Problem ist doch, daß ich der Methode "enterNumber" ein NSNumber-Objekt (enthält ein int) übergebe und die Prüfung mit isKindOfClass NO ergibt. Das verstehe ich einfach nicht.

    Dann verstehe ich auch nicht, warum die Prüfung mit respondsToSelector:@selector(selectedTag) YES ergibt. Zum einen sollte meine NSNumber (oder was daraus geworden ist) nicht auf selectedTag reagieren, zum anderen tut sie es auch nicht, obwohl die Prüfung darauf positiv war.

    Wenn ich wieder die GUI aktiviere (in der main.m), dann funktioniert die Prüfung mit respondsToSelector.

    Ich bin jetzt wirklich verwirrt und der Doku von Apple bekomme ich nichts entlockt. Zum Thema NSCFNumber finde ich selbst in der Volltext-Suche KEINE Einträge.

    Stelle ich mich gerade nur dumm an, oder falle ich da auf ein linkes Detail rein, oder was?
  • Original von Agrajag
    Ich bin jetzt wirklich verwirrt und der Doku von Apple bekomme ich nichts entlockt. Zum Thema NSCFNumber finde ich selbst in der Volltext-Suche KEINE Einträge.
    NSCFNumber ist eine interne Klasse die auf Core-Foundation-Objekte abbildet. Wenn da irgendwo etwas in der Doku von "toll-free-bridged" steht, dann ist das gemeint. NSCFNumber ist eine Unterklasse von NSNumber und versteht alle deren Methoden (und keine andere). D.h. der Fehler ist ein anderer.

    Schon mal den Debugger ausprobiert, einen Breakpoint auf eine Action-Methode gesetzt und im Single-Step durch den Code gelaufen?

    -- hns
  • Original von Tom9811
    NSNumber ist ein Class-Cluster. Unter anderem wird auf NSCFNumber gemappt.

    Aber woher weiß ich, auf welche Methoden dann das NSCFNumber noch reagiert. Irgendwie macht es für mich keinen Sinn. Wieso kann ich jetzt nicht (so wie du es ja selbst in deinem Beispiel genannt hast), einfach auf die Klassenzugehörigkeit zu NSNumber prüfen? Wenn ich auf NSCFNumber teste, dann ist sie nicht bekannt (Compiler-Fehler). Bei Java kann ich mich darauf verlassen, daß ein Integer ein Integer ist. Wieso geht es hier nicht? Wo liegt darin der Vorteil/Sinn? Und vor allem, wie gehe ich nun damit um? Irgendwie muß ich ja testen, um was es sich beim Sender handelt.

    Es scheint mir so, als ob ich nicht auf den Typ testen sollte, sondern darauf, ob das Objekt auf intValue reagiert.