Fortschrittsbalken tut nicht

  • Ich habe mir die Berechnung nicht ins Detail angesehen, aber mir sind drei Dinge aufgefallen:

    Erstens greifst Du anscheinend aus dem Hintergrundthread auf GUI-Elemente zu (z.B. xyz_ausgabe setStringValue usw.). Das kann zu oben beschriebenen Ärger führen, denn GUI-Elemente sind i.d.R. nicht thread-safe, sprich: Auf sie darf ohne weiteres nur aus dem Hauptthread zugegriffen werden. Kommunikation zwischen Threads ist nicht ganz trivial - da können die lustigsten Sachen passieren. Der Fluch des Multithreading.

    Zweitens nutzt Du teilweise NSCalendar und teilweise berechnest Du Daten selbst (Schaltjahre und die 86400-Konstante). Lass' alles vom NSCalendar machen, der kümmert sich um viele Details, die man selbst nicht berücksichtigen will - nicht alle Tage sind 86400 Sekunden lang (Sommerzeit, Schaltsekunden usw.).

    Und drittens tut Deine Schleife sehr viel Zeug, das nicht gebraucht wird (z.B. erzeugst Du für jeden Tag eine Menge Strings, die nur für zwei Tage im Jahr gebraucht und sonst wieder verworfen werden). Einige Sachen sind für alle Durchläufe gleich, die können aus der Schleife rausgezogen werden. Ich weiß nicht, für wie viele Jahrtausende Du Zinsen berechnen willst, aber wenn man die Berechnung effizient umschreibt sollte sie für alle erdenklichen Zeiträume so schnell sein, dass man gar keinen Fortschrittsbalken und auch keinen Hintergrundthread braucht.
    Multigrad - 360°-Produktfotografie für den Mac
  • Hi,
    ich bin mal so frei und geb noch ein paar zusätzliche Anregungen:

    1. Wenn Du setFloatValue: verwendest, dann sollte das Argument auch vom Typ float sein und nicht vom Typ double. Alternativ kannst Du natürlich setDoubleValue: verwenden.
    2. "nil" wird für Objekte verwendet. Für C Zeiger (void *), (int *) usw. solltest du "NULL" verwenden. Für Klassen gäbe es dann auch noch "Nil".
    3. Wie schon beschrieben ist die Zeitrechnung sehr Komplex und scheinbar selbstverständliches (86400) ist es in wahrheit nicht. Daher solltest Du soviel wie möglich von den Datumsklassen erledigen lassen.
    4. Deine Method um das halbjahrflag zu setzten ist sehr ungewöhlich, wieso niicht einfach halbjahrsflag = ([comp month] < 7 ? 1 : 2) oder noch einfacher BOOL istImErstenHalbjahr = ([comp month] < 7). Da du das Flag ja eh berechnest, solltest Du es natürlich auch im switch verwenden.
    5. Evtl ist es besser, die Zinssätze in eine Array zu legen, dann kannst Du sie auch einfach einlesen.
    6.Deine Methode rechnen: greift -aus dem Hintergrund Thread - direkt auf die GUI zu. Du nimmst also an, dass alle GUI Element Thread safe sind, dass ist aber nicht der Fall. Um dieses Problem zu beheben kannst Du in der Action einen Snapshot der aktuellen GUI Einstellungen erstellen und diesen entweder im self Ablegen - die Properties sollten dann aber auch Threadsafe sein - oder besser noch in ein separates Objekt auslagern - dann können die Properties auch nonatomic sein - welches als Parameter(withObject:) an die Berechnungsroutine übergeben wird.
    7 Generell solltest Du dir aber überlegen, welche Grössen in der Schleife berechnet werden müssen und welche du vor der Schleife berechnen kannst.
  • Hallo Leute, danke für Eure Anregungen und Tipps. Ich werde versuchen, sie umzusetzen, Ich bin erstmal froh, dass alles so läuft, wie ich möchte.

    Das ist ein (Wieder)Lernprojekt. Ich bin seit über 10 Jahren raus aus der Programmierung und auch nicht mehr der Jüngste ;) Es kommt jetzt alles so Stück für Stück wieder und dazu noch das, für mich absolut neue Objective-C. Ich kämpfe ;)
    Ich bin gegen Signaturen!!!
  • Ich muss hier nochmal ansetzen: Ich kriege immer noch die Warnung "CoreAnimation: warning, deleted thread with uncommitted CATransaction; set CA_DEBUG_TRANSACTIONS=1 in environment to log backtraces." auf der Konsole und manchmal, nicht immer, crashed das Programm.
    Es muss irgendwas mit dem Progressindicator zu tun haben. Wenn ich alles, was damit zu tun hat, rausschmeiße, funktioniert es.

    Gibts noch Tipps?
    Ich bin gegen Signaturen!!!
  • Moin zusammen,

    ich muss nochmal anknüpfen hier, da es immer noch Fehler gibt in meiner Anwendung, welcher mit performSelectorInBackground und/oder performSelectorOnMainThread zusammenhängt und ich den partout nicht finde.
    Bei jeder Berechnung erhalte ich die o.g. CoreAnimation Warnung und sporadisch (mal bei der 1., mal bei der 5. oder 12.) crasht die Anwendung mit einem Bad Exec (siehe Screenshot). Die Eingabewerte sind dabei bei jeder Berechnung gleich.

    @MCDan: Würdest Du Dir das evtl. mal anschauen?

    Viele Grüße
    Bernd
    Ich bin gegen Signaturen!!!
  • Jupp der muss auf jeden Fall um alles drum herum was in Hintergrund läuft. Das hat nichts mit ARC zu tun also

    Quellcode

    1. -(void)backGroundMethode
    2. @autoreleasepool
    3. {
    4. Code…
    5. }
    6. return;


    Und geh mal im Navigator links auf die Breakpoints und klicke unten auf das "+" button. Dann sagst Du Exception-Brakpoints und bestätigst das aufgehende Fenster einfach mit OK. Danach sollte die App beim Absturz genauer anzeigen wo der Fehler liegt.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • ok, nachdem der @autoreleasepool drin ist, stürzt es schonmal nicht mehr ab, jedenfalls nicht "unkontrolliert".

    Wenn ich allerdings bei der Berechnung einen größeren Endewert eingebe, so dass die Berechnung länger dauert und dann wie wild auf dem Action-Button rumklicke, bevor eine Berechnung abgeschlossen ist, kommt die Bad Access immer noch. Kann man das irgendwie verhindern, dass der User sowas tut? Also irgendwie den Button blockieren, solange berechnet wird.

    Die Warnung ist durch den @autoreleasepool nicht verschwunden.

    Gruß
    Bernd
    Ich bin gegen Signaturen!!!
  • Ahhh, ich komme dem Problem etwas näher, glaube ich. Kann natürlich auch sein, dass sich hier ein Neues auftut.

    Ich habe NSTextField, in das ein Betrag eingegeben wird (double). Dieses setze ich zu Beginn in applicationDidFinishLaunching auf 1000 und initialisiere damit das Eingabefeld. Solange ich berechne, funktioniert das. Sobald ich aber etwas in das Feld eingebe, also einen anderen Betrag und lasse rechnen, crasht es sofort. Und zwar immer an der gleichen Stelle. Damit kann ich aber nix anfangen.
    Ich bin gegen Signaturen!!!
  • Also es fängt schonmal damit an, dass das alles nicht ins Appdelegate gehört sondern in einen WindowController.

    Dann kannst du nicht im didfinoshLaunshing das Textfield setzen, weil es das da ja noch gar nicht gibt. Wie erstellst du denn überhaupt das Fenster ? Machst du ein addWindow oder was ?

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Naja, ich hab ein @property (assign) IBOutlet NSWindow *window;
    Bis vor dem Progressindicator hat ja auch alles funktioniert. Die Probleme gingen erst damit los ;)

    Wieso kann man das nicht ins AppDelegate schreiben? Es scheint ja zu funktionieren, die Werte werden ja gesetzt.

    Quellcode

    1. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
    2. {
    3. [ende_eingabe setDateValue: [NSDate date]];
    4. startbetrag = 1000;
    5. [betrag_eingabe setFloatValue:startbetrag];
    6. dynzins = 2;
    7. [dynzins_eingabe setFloatValue:dynzins];
    8. festzins = 10;
    9. [festzins_eingabe setFloatValue:festzins];
    10. NSBundle *mainBundle = [NSBundle mainBundle];
    11. [info_bundlename setStringValue: [mainBundle objectForInfoDictionaryKey:@"CFBundleName"]];
    12. [info_copyright setStringValue: [mainBundle objectForInfoDictionaryKey:@"NSHumanReadableCopyright"]];
    13. [info_version setStringValue:[NSString stringWithFormat:@"Version: %@ (%@)", [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"],[mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"]]];
    14. }
    Alles anzeigen
    Ich bin gegen Signaturen!!!
  • Siehe Samstag: MIt einem Thread wird nicht nur die Berechnung im Hintergrund ausgeführt, sondern sämtliche Zugriffe auf geteilte Ressourcen müssen synchronisiert werden. Es müssen also Locks oder Queues verwendet werden.

    Viel einfacher: Modal-Sessions.

    BTW: Ich habe auch im Kopf, dass PIs threadfest sind, finde es aber ebenso wenig wie Tibor in der aktuellen Doku.
    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"?
  • beage schrieb:

    @Amin: Ich bin ja fast geneigt, den ganzen Progressindicatorsch... wieder rauszuschmeißen! Damit gingen die Probleme nämlich erst los.

    Musst du nicht. Mach einfach eine modale Session daraus. Das sind ein paar wenige Zeilen, du hast keine Probleme mit Threads, keine Probleme mit Queues, keine Probleme mit Locks und Deadlocks.

    Ich habe Samstag genau so einen Fall als Beispiel für die fehlerhafte Verwendung von Nebenläufigkeit genommen. Das hat mit Threads usw. nichts zu tun. Die brauchst du nur, wenn du willst, dass der Nutzer während der Berechnung ganz normal mit deinem Programm weiterarbeiten kann oder du die Berechnung auf mehrere Kerne verteilen möchtest. Aber wenn du das willst, reicht es nicht aus, mal eben einen Thread zu starten.
    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"?