Xcode11 OSX Swift 5 Progammlogik

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Xcode11 OSX Swift 5 Progammlogik

    Hallo,

    in meinem Testprogramm (arbeite mit einem Storyboard) habe ich folgenden Ablauf:
    1. Button "GetDataFromDBTable"
    2. Button "ShowData"
    3. Button "Delete Data View"

    Das funktioniert alles bestens.

    In meiner App möchte ich aber nicht, dass der Anwender das Auslesen der Daten aus der DB mit einem separaten Button starten muss.

    Wenn ich jedoch die Funktion, welche durch den ersten Button "GetDataFromDBTable" aufgerufen wird, im zweiten Button "ShowData" aufrufe, dann erhalte ich keine Daten! Erst beim zweiten Mal werden mir die Daten aus der DB angezeigt. :/

    Mit dem Debugger konnte ich feststellen, dass die Funktion die Daten liefert. Dies ist jedoch erst der Fall, nachdem die ACTION des Buttons "ShowData" abgearbeitet wurde. ?(

    Was beachte ich hier nicht bzw. wie kann ich dies beheben?
  • MCDan schrieb:

    OSXDev schrieb:

    Problem gelöst. Habe die Asynchrone Verarbeitung innerhalb der Funktion entfernt und nun funktioniert es einwandfrei. :D
    Hauptsache der Main Thread wird dadurch nicht angehalten/blockiert. ;)
    Hmmm, bei größeren Datenmengen stellt sich tatsächlich heraus, dass der Main Thread kurzzeitig angehalten wird. Unschön. ?(

    Also doch wieder Asynchrone Verarbeitung einfügen. Das bringt mich nun wieder zur ursprünglichen Fragestellung s.o..



    P.S.: Gibt es eine weitere Möglichkeit, innerhalb des Main Threads auf das Ergebnis des Asynchronen Threads zu warten - ohne den Einsatz von Semaphoren und Dispatch.global().asnc -, bevor mit der Verarbeitung begonnen wird?

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von OSXDev ()

  • Du könntest den Button showData erst aktivieren wenn die Daten geladen wurden (completion Block) oder, und das würde ich bevorzugen, du machst es wie bisher und musst deine View die die Daten anzeigt aktualisieren wenn sie da sind. Bei einer Table- oder CollectionView also ein reloadData, sofern du sie nicht mit Bindings ansprichst sondern die Delegate-/Datasource Methoden nutzt.
    Das Herz besitzt Gründe, die die Vernunft nicht kennt.
  • Ich muss meine Frage konkretisieren. ;)

    Quellcode

    1. URLSession.shared.dataTask(with: url) { (data, response, err) in
    2. guard let data = data else {return}
    3. do{
    4. let courses = try
    5. JSONDecoder().decode([Course].self, from: data!)
    6. arrayCourses = courses
    7. catch let jsonErr{
    8. print("Error serializing json: ", jsonErr)
    9. }
    10. }.resume()
    Alles anzeigen
    Der obige Code wird asynchron ausgeführt bzw. erst gestartet wenn .resume() aufgerufen wird. Somit wird unteranderem sichergestellt, dass der Main Thread nicht unterbrochen wird.

    Für die weitere Verarbeitung bedeutet dies jedoch, dass ich warten muss bis die Daten vollständig vom Decoder aufbereitet und im Array arrayCourses gespeichert wurden. Nur wie? ?(

    Erreichen kann ich dies, wenn ich folgende Zeilen auskommentiere 1 bis 3, 11. und in Zeile 1. nachfolgenden Code einfüge:


    Quellcode

    1. let data = try? Data(contentsOf: url)

    Hierdurch wird aber der Main Thread unterbrochen und zwar je größer die aufzubereitenden Datenmenge desto länger hält die Unterbrechung an.
    Unschön! ;(

    Welche Möglichkeiten gibt es noch bzw. wie verhindere ich dies?
  • OSXDev schrieb:

    Quellcode

    1. URLSession.shared.dataTask(with: url) { (data, response, err) in
    2. guard let data = data else {return}
    3. do{
    4. let courses = try
    5. JSONDecoder().decode([Course].self, from: data!)
    6. arrayCourses = courses
    7. // Hier sind deine Daten geladen und verarbeitet also sollte sowas folgen
    8. DispatchQueue.main.async {
    9. // aktualisiere die View die die Daten anzeigt.
    10. }
    11. catch let jsonErr{
    12. print("Error serializing json: ", jsonErr)
    13. // Aktualisiere die View die die Daten anzeigt und kommuniziere den Fehler
    14. }
    15. }.resume()
    Alles anzeigen
    Schau mal die Kommentare im Code
    Das Herz besitzt Gründe, die die Vernunft nicht kennt.
  • pierredrks schrieb:

    Du könntest den Button showData erst aktivieren wenn die Daten geladen wurden (completion Block) oder, ....
    Habe Deinen Post doch glatt übersehen, weil ich mit meiner Ausführung wohl zu beschäftigt war. Aber Dein Hinweis auf den ersten Lösungsvorschlag würde ich gerne umsetzen, nur sagst mir der Begriff "completion Block" nicht wirklich etwas. Gefunden habe ich folgenden Code (compeltionHandler) s. u.:


    Quellcode

    1. // Function called from the completion reference
    2. func completionHandler(value: Int) {
    3. print("Function completion handler value: \(value)")
    4. }
    5. // The computation function
    6. func computeValue(start: Int, completion: (Int) -> Void) {
    7. var start = start
    8. for _ in 1...100 {
    9. start += 1
    10. }
    11. completion(start) // completion -> completionHandler(value: Int) -> Void
    12. }
    13. // Compute a value and then send the finished value to the function completion handler
    14. computeValue(start: 1, completion: completionHandler)
    Alles anzeigen
    Meinst Du diesen Ablauf? Wenn ja, dann hätte ich da noch ein paar Fragen. In Zeile 17 wird die Funktion computeValue mit einem Wert und einer weiteren Funktion aufgerufen. Wenn ich dies nun richtig interpretiere, wird erst die For-Schleife ausgeführt und danach die Funktion completionHandler mit dem Wert der Variablen start (Wert beträgt zu diesem Zeitpunkt 100) aufgerufen und ausgeführt.

    Ergo handelt es sich um eine synchrone Abarbeitung? Wenn dem so ist, wird dann nicht der Main Thread für die Zeit der Abarbeitung angehalten oder verstehe ich hier etwas nicht richtig? ?(

    P.S.: Wie kann ich den completionHandler in meine Funktion integrieren? Die Abarbeitung erfolgt doch asynchron, wie bekomme ich dies unter einen Hut? ;(

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von OSXDev ()

  • Upps - sorry, bin wohl noch nicht richtig wach und erst einmal ein Dankeschön für Deine Unterstützung. :thumbup:

    pierredrks schrieb:

    Du könntest den Button showData erst aktivieren wenn die Daten geladen wurden (completion Block) oder, und das würde ich bevorzugen, du machst es wie bisher und musst deine View die die Daten anzeigt aktualisieren wenn sie da sind. Bei einer Table- oder CollectionView also ein reloadData, sofern du sie nicht mit Bindings ansprichst sondern die Delegate-/Datasource Methoden nutzt.

    Da habe ich wohl etwas missverstanden. ;( Verhält es sich dann nicht so, dass ich für beide von Dir beschriebenen Vorgehensweisen Deinen ergänzen Code verwenden muss? Wenn dies der Fall ist, wie veranlasse ich dann, dass beim Betätigen des zweiten Buttons mit der Aktualisierung der Ansicht gewartet wird bis die Daten vorhanden sind bzw. die Abarbeitung nicht asynchron durchgeführt bzw. fortgesetzt wird? Ich glaube ich habe ich ein Verständnisproblem :?:

    P.S.: Hole mir jetzt noch einen Café und hoffe auf eine Eingebung. :saint:
  • Genau. Für die Variante mit dem Button aktivieren würdest du meinen Kommentar in etwa mit

    Quellcode

    1. showDataButton.active = true
    ersetzen.

    Bei der zweiten Variante lädst deine View neu.


    OSXDev schrieb:

    Wenn dies der Fall ist, wie veranlasse ich dann, dass beim Betätigen des zweiten Buttons mit der Aktualisierung der Ansicht gewartet wird bis die Daten vorhanden sind bzw. die Abarbeitung nicht asynchron durchgeführt bzw. fortgesetzt wird?
    Das passiert doch automatisch. Beim Klick lädst du die Daten neu und erst wenn die Daten da sind wird der completion Handler ausgeführt (Der Teil wo du das JSON parst).
    Das Herz besitzt Gründe, die die Vernunft nicht kennt.
  • pierredrks schrieb:

    Genau. Für die Variante mit dem Button aktivieren würdest du meinen Kommentar in etwa mit

    Quellcode

    1. showDataButton.active = true
    ersetzen.

    ...
    Hmmm, ich habe dies so umgesetzt und erhalte nun folgende Fehlermeldung: NSControl.isEnabled must be used from main thread only.

    Die Ergänzung habe ich innerhalb von Dispatch.main.async vorgenommen. Warum erhalte ich obige Fehlermeldung. Ich dachte, dass der Code innerhalb von Dispatch im Main Thread ausgeführt wird??? ?(
  • Quellcode

    1. URLSession.shared.dataTask(with: url) { (data, response, err) in
    2. guard let data = data else {return}
    3. do{
    4. let courses = try
    5. JSONDecoder().decode([Course].self, from: data!)
    6. arrayCourses = courses
    7. // Hier sind deine Daten geladen und verarbeitet also sollte sowas folgen
    8. print("\n\nCourses: ", courses)
    9. DispatchQueue.main.async {
    10. // aktualisiere die View die die Daten anzeigt.
    11. self.outletGetDataFromDB.isEnabled = true
    12. print("\n\nDo ArrayCourses", arrayCourses)}
    13. }
    14. catch let jsonErr{
    15. print("Error serializing json: ", jsonErr)
    16. // Aktualisiere die View die die Daten anzeigt und kommuniziere den Fehler
    17. self.labelHinweis.stringValue = "JSON-Error"
    18. }
    19. }.resume()
    Alles anzeigen
    Ich habe mal Zeile 14 auskommentiert. Danach läuft es und die Ausgabe via print() liefert auch Ergebnisse. ?(


    Der nachfolgende Code läuft doch im Kontext des Main Threads? Das auszugebende Array ist jedoch nicht gefüllt? =O

    Quellcode

    1. @IBAction func actionGetDataFromDB(_ sender: Any) {
    2. print("Action-ArrayCourses", arrayCourses)
    3. }
    Irgendwie verstehe ich es nicht??? Warum kann ich innerhalb von Dispatch() nicht auf Objekte im Main Thread zugreifen und warum erhalte ich keine Daten?

    Das Array ist innerhalb des VC - also sogar global - definiert und dennoch funktioniert es nicht. Innerhalb der Klasse funktioniert es erst gar nicht. So will ich es eigentlich nicht. Globale Variablen sind mit Vorsicht zu verwenden. Ich habe dies nur für einen Test mal probeweise durchgeführt. :(

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von OSXDev () aus folgendem Grund: Nachtrag

  • Das dürfte so gar nicht kompilieren, da fehlt noch die schließende Klammer der DispatchQueue.

    Quellcode

    1. DispatchQueue.main.async {
    2. // aktualisiere die View die die Daten anzeigt.
    3. self.outletGetDataFromDB.isEnabled = true
    4. }
    Das Label im catch Teil muss auch ein Dispatch Konstrukt.
    Das Herz besitzt Gründe, die die Vernunft nicht kennt.
  • Den Xcode-Ordner habe ich mal archiviert und im Anschluss gelöscht. Sämtliche Cach-Odrner wurden ebenfalls archiviert; gelöscht und Xcode 11.2 nochmals installiert.

    Dies ist das zweite Mal, dass ich zu so einer drastischen Massnahme greifen musste! :( Schade um die verlorene Lebenszeit. Den Apple Support habe ich kontaktiert und die Daten zur Verfügung gestellt. Bin gespannt woran es letztendlich lag.

    Das hat mich völlig konfus gemacht und zweifeln lassen ob ich meinen eigenen Code noch verstehen. Nun gut, der obige Code funktioniert einwandfrei; ohne Fehler - die Daten sind nun auch im Main Thread verfügbar. :thumbup:

    Zukünftig werde ich bei größeren Problemen erst einmal das Projekt auf einem zweiten Mac starten, bevor ich mich auf die Fehlersuche begebe. :S

    Danke für Eure Hilfestellungen. :thumbsup: