Programm wieder ansprechbar machen

  • Programm wieder ansprechbar machen

    Hi,
    in meinem kleinen Progrämmchen werden recht lange irgendwelche Berechnungen angestellt (ich will das nicht unbedingt Algorithmus nennen :D). Das kann einige Zeit dauern. Während diesen Berechnungen ist das Programm absolut nicht mehr ansprechbar und irgendwie sagt mir die Aktivitätsanzeige auch, dass das Programm nicht mehr reagiere.
    Zu allem Überfluss sehe ich dadurch auch nicht die Ausgabe meines Programmes im TextView.

    Gibt es eine Möglichkeit, dass das Programm weiterhin ansprechbar bleibt?

    Danke für eure Hilfe,
    Chris

    PS: Wahrscheinlich wird mindestens einer mir sagen, dass ich das mit Threads machen soll. Gibt es da nicht vllt. noch eine andere Möglichkeit?
  • ok, dann will ich mal der sein der dir sagt dass du es mit threads machen sollst.
    Das ist ganz einfach. Du nimmst NSThread her und deatachest eine funktion in einen neuen thread. von da aus kannst du dann machen was du willst und das programm reagiert trotzdem weiter. wenn du was an der UI verändern willst (also zb ein textview aktualisieren) dann musst du das im hauptthread machen. du sagst einfach im thread performSelectorOnMainThread:withObject:waitUntilDone: oder so in der art (war jetzt nur aus dem kopf)

    also zb:

    [textField performSelectorOnMainThread:@selector(setStringValue:) withObject:@"Neuer wert" waitUntilDone:YES];

    mehr brauchts dazu nicht.

    Zu deiner anderen frage: Ja, es würde noch anders gehen und zwar wenn du zwischen deinen berechnungen immer mal wieder zeitgeben würdest für die den RunLoop aber das ist nicht gerade das was ich dir so empfehle...
  • RE: Programm wieder ansprechbar machen

    Jepps, Threads sind das richtige Stichwort. Du fürhst die (Root-)Methode in einem anderen Thread aus. Das Problem liegt eigentlich eher darin, dein Model konsistennt zu halten. Um es kurz zu machen:

    Wenn du Threads hast, werden zwei Kontrollflüsse nebenläufig ausgeführt. Dies bedeutet, dass nach jeder Anweisung in dem Thread eine Anweisung im "Hauptprogramm" (welches einfach nur ein anderer Thread ist) ausgeführt werden kann. Beide greifen aber auf die gleichen Daten zu. Es ist leicht vorzustellen, dass, wenn du in beiden Programmteilen die gleichenn Daten abholst und in beiden veränderst, das Ergbnis beim Zurückschreiben eher zufälliger Natur sind. Ausweg sind die so genanten Locks (NSLock).

    Wenn du NSThread in der Doku suchst, wird es sicher eine Einführung von Apple geben.
    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"?
  • Hi,
    danke für eure Hinweise.

    Ist zwar dann mal wieder ein wenig Arbeit, aber klingt plausibel. ;)

    Aber noch eine Frage:
    muss ich den NSThread im InterfaceBuilder subclassen, um dann Outlets anzulegen und bspw. mit dem TextField zu verbinden oder wie greife ich in meinem Thread auf die von gritsch beispielhaft genannte textField-Instanz zu?

    Chris
  • Original von CalganX
    Hi,
    danke für eure Hinweise.

    Ist zwar dann mal wieder ein wenig Arbeit, aber klingt plausibel. ;)

    Aber noch eine Frage:
    muss ich den NSThread im InterfaceBuilder subclassen, um dann Outlets anzulegen und bspw. mit dem TextField zu verbinden oder wie greife ich in meinem Thread auf die von gritsch beispielhaft genannte textField-Instanz zu?

    Chris


    wie tom gerade gesagt hat griefst du auf die selbe instanzvariablen zu wie auch dein hauptThread. Du merkst also keinen unterschied ob deine funktion in einen thread ausgelagert ist oder nicht. außer eben bei dem (schreibenden) Zugriff auf instanzvariablen was man in den meisten fällen sehr einfach organisieren kann
  • Nein, einen Thread im IB subclassen und -- das meinst du wohl -- instanzieren ist eher strange. Der Sinn von Threads ist ja die Nebenläufigkeit. Und von diesem Standpunkt ausgehend, können auch mehrere Threads nebenläufig sein. Das geht im IB nicht.

    Zurück zum konkreten Problem:
    a) Es ist eigentlich keine so gute Idee, Threads direkt auf das UI zugreifen zu lassen. Bedenke, dass sie nebenläufig sind. Das kann eine Interface-Keilerei geben. Dein User will das nicht.
    b) Zur Lösung des Problems musst du einen Kommunikationskanal zwischen dem Thread und dem Hauptprogramm erstellen. Wenn du mit Bindings arbeitest, dann wird ja ohnehin jede Aktualisierung des MModels automatisch von den Bindings gehandhabt. Wenn du klassisch mit Controllern arbeitest, solltest du über eine "Signal-Variable" nachdenken, die du regelmäßig lesen kannst.
    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"?
  • Original von Tom9811
    a) Es ist eigentlich keine so gute Idee, Threads direkt auf das UI zugreifen zu lassen. Bedenke, dass sie nebenläufig sind. Das kann eine Interface-Keilerei geben. Dein User will das nicht.


    also wenn ich eine berechnung durchführe, dabei ein modal-sheet öffne und dort nen statusbalken anzeige und eventuell noch ein textfeld mit ner beschreibung dann aktualisier ich das sehr wohl aus dem thread heraus mit der besagten performOnMainThread-Funktion.

    dann ises fein wenn man einige NSProgressIndicatior-Additions hat wie zb:

    - (void)setMaxValue:(NSNumber *)maxValue;
    - (void)setCurrentValue:(NSNumber *)currentValue;
    - (void)incrementByOne;
    - (void)setIndeterminate;
    - (void)setDeterminate;

    ;)
  • a) Es ist eigentlich keine so gute Idee, Threads direkt auf das UI zugreifen zu lassen. Bedenke, dass sie nebenläufig sind. Das kann eine Interface-Keilerei geben. Dein User will das nicht.

    Das Problem kenne ich... allerdings sehe ich da keine schöne Möglichkeit das sinnvoll anders zu gestalten.
    Wie gesagt: ich habe ein NSTextView, in dem mein Algorithmus immer wieder ausgibt, was er gerade gemacht hat (Einkauf, Verkauf, aktuelles Guthaben etc.). Der Algorithmus ist derzeit einfach eine Methode meines AppControllers, die aufgerufen wird, sobald auf den Button geklickt wurde (über 'ne Action halt).
    Ich wüsste jetzt nicht, wie ich das Gleiche realisieren kann, ohne den Thread auf mein Interface zugreifen zu lassen.

    b) Zur Lösung des Problems musst du einen Kommunikationskanal zwischen dem Thread und dem Hauptprogramm erstellen. Wenn du mit Bindings arbeitest, dann wird ja ohnehin jede Aktualisierung des MModels automatisch von den Bindings gehandhabt. Wenn du klassisch mit Controllern arbeitest, solltest du über eine "Signal-Variable" nachdenken, die du regelmäßig lesen kannst.

    Öhm... wann arbeite ich mit Bindings, wann mit Controllern? Ich schätze mal, dass ich mit Bindings arbeite, da Controller, wenn ich mich richtig erinnere, ja die "ältere" Ausgabe von Outlets und Actions sind.
    Und was ist das MModel? ;)
    Zur Kommunikation hatte ich mir gedacht, könnte ich ein NSLock verwenden, wie bereits vorgeschlagen. So steht's auch in der Doku drin.

    Chris
  • Original von CalganX
    a) Es ist eigentlich keine so gute Idee, Threads direkt auf das UI zugreifen zu lassen. Bedenke, dass sie nebenläufig sind. Das kann eine Interface-Keilerei geben. Dein User will das nicht.

    Das Problem kenne ich... allerdings sehe ich da keine schöne Möglichkeit das sinnvoll anders zu gestalten.
    Wie gesagt: ich habe ein NSTextView, in dem mein Algorithmus immer wieder ausgibt, was er gerade gemacht hat (Einkauf, Verkauf, aktuelles Guthaben etc.). Der Algorithmus ist derzeit einfach eine Methode meines AppControllers, die aufgerufen wird, sobald auf den Button geklickt wurde (über 'ne Action halt).
    Ich wüsste jetzt nicht, wie ich das Gleiche realisieren kann, ohne den Thread auf mein Interface zugreifen zu lassen.


    Gehen wir mal davon aus, dass dieses textView nicht editierbar ist (für den user) und du auch im hauptthread nicht darin rumwerkelst dann besteht ja kein problem wenn du dir ne methode machst:

    - (void)appendAndScroll:(NSString *)newMessage;

    diese dann mitels performSelectorOnMainThread aufrufst und eben deine message dranhängst und nach unten scrollen lässt damit die message auch sichtbar ist...
    Meiner meinung nach muss man immer selbst entscheiden was man tut und was nicht. Es ist auch verboten bei rot über die ampel zu gehen oder über die straße zu gehen wo kein zebrastreifen ist - ich tus aber trotzdem wenn weit und breit kein auto (und polizist) um die wege ist... ;)
  • DANGER, WILL ROBINSON!

    Viele Elemente aus dem AppKit sind in dem Sinne nicht thread safe, als dass sie aus dem Main Thread angesprochen werden müssen.

    Das macht man am einfachsten mit performSelectorOnMainThread:

    Alex
    The only thing that really worried me was the ether.
  • Hi,
    okay, ich habe das jetzt mal versucht:

    Quellcode

    1. - (void)appendAndScroll:(NSString *)newMessage
    2. {
    3. // TextView erweitern:
    4. [viewOutput setString:[[viewOutput string] stringByAppendingString:newMessage]];
    5. // Zum Ende scrollen:
    6. }
    7. - (IBAction)evaluate:(id)sender
    8. {
    9. // Ausgabe leeren
    10. [viewOutput setString:@""];
    11. // Evaluierungsprozess in Thread auslagern und starten
    12. [NSThread detachNewThreadSelector:@selector(evaluationThreadMethod:) toTarget:[self class] withObject:nil];
    13. }
    14. - (void)evaluationThreadMethod:(id)anObject
    15. {
    16. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    17. // "Inventar" des Vereins
    18. inventory = [NSMutableArray array];
    19. /* ... */
    20. /* Irgendwo dazwischen steht dann mal: */
    21. [self performSelectorOnMainThread:@selector(appendAndScroll) withObject:
    22. [NSString stringWithFormat:@"Gegenstand %d wurde eingekauft (-%d Euro)\n", idx, [[currentItem price] intValue] ]
    23. waitUntilDone:YES];
    24. /* ... */
    25. [pool release];
    26. }
    Alles anzeigen


    Allerdings bekomme ich beim Ausführen jetzt im Runlog folgende Fehle um die Ohren gehauen:

    Quellcode

    1. [Session started at 2006-10-05 23:42:51 +0200.]
    2. 2006-10-05 23:42:57.667 bwinf25_1[2533] *** _NSAutoreleaseNoPool(): Object 0x38d4c0 of class NSCFString autoreleased with no pool in place - just leaking
    3. 2006-10-05 23:42:57.668 bwinf25_1[2533] *** +[AppController evaluationThreadMethod:]: selector not recognized
    4. 2006-10-05 23:42:57.668 bwinf25_1[2533] *** _NSAutoreleaseNoPool(): Object 0x365cf0 of class NSCFString autoreleased with no pool in place - just leaking
    5. 2006-10-05 23:42:57.668 bwinf25_1[2533] *** _NSAutoreleaseNoPool(): Object 0x367f30 of class NSCFString autoreleased with no pool in place - just leaking
    6. 2006-10-05 23:42:57.668 bwinf25_1[2533] *** _NSAutoreleaseNoPool(): Object 0x38f350 of class NSException autoreleased with no pool in place - just leaking
    7. 2006-10-05 23:42:57.668 bwinf25_1[2533] An uncaught exception was raised
    8. 2006-10-05 23:42:57.668 bwinf25_1[2533] *** +[AppController evaluationThreadMethod:]: selector not recognized
    9. 2006-10-05 23:42:57.668 bwinf25_1[2533] *** Uncaught exception: <NSInvalidArgumentException> *** +[AppController evaluationThreadMethod:]: selector not recognized


    Was mach ich damit jetzt? ;)

    Chris
  • RE: Programm wieder ansprechbar machen

    Hui, Ihr seid ja mittlerweile schon ganz woanders, aber zur ürsprünglichen Frage: Ja, es gibt eine Möglichkeit ohne Threads: Ab und zu in der Berechnung

    Quellcode

    1. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantPast]];

    aufrufen. Dann wird der Main Run Loop angetickt, was dazu führt, dass OSX nicht mehr glaubt, Dein Programm sei stecken geblieben...
    Multigrad - 360°-Produktfotografie für den Mac
  • erstens keine Klasse übergeben sondern deine instanz also self nicht [self class]

    den richtigen selector verwenden

    appendAndScroll: der doppelpunkt ist wichtig

    dann sollte es gehen ^^

    edith meint:
    gritsch hör doch mal auf immer gleichzeitig mit mir zu posten X( ich will doch auch mal was schlaues sagen ^^;;;

    och menno ^^;
    snafu
    :() { :|: &};:
    sometimes i dream in hex
    Obey gravity! Because its a law!
  • RE: Programm wieder ansprechbar machen

    Original von mattik
    Hui, Ihr seid ja mittlerweile schon ganz woanders, aber zur ürsprünglichen Frage: Ja, es gibt eine Möglichkeit ohne Threads: Ab und zu in der Berechnung

    Quellcode

    1. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantPast]];

    aufrufen. Dann wird der Main Run Loop angetickt, was dazu führt, dass OSX nicht mehr glaubt, Dein Programm sei stecken geblieben...


    ja klar gehts, hab ich ja gesagt. Aber das ein problem: was verstehst du unter "ab und zu"? auf nem schnellen rechner arbeitet er etwas in ner halben sekunde ab was ein älterer schon mal 10 sekunden braucht. Dann gibt es auch noch die operationen die man nicht so einfach aufteilen kann...