Wie oft wird "awake from nib" ausgeführt

M

michael m.

Aktives Mitglied
Thread Starter
Dabei seit
29.12.2006
Beiträge
407
Reaktionspunkte
6
Hi,

mittlerweile bin ich dabei mein Konsolenprogramm zu einem Desktopprogramm um zu schreiben und erlerne nun die Möglichkeiten mit einer Benutzeroberfläche.

Ich habe gelesen, das die Methode "awakeFromNib" immer als erstes ausgeführt wird und meist für die Initialisierung einer Oberfläche benutzt wird.
Daher habe ich Code geschrieben, der mit dem Aufbau des Fensters einige Variablen füllt und nach vorhandenen Dateien Ausschau hält.

Code:
// Führt diesen Code als erstes aus, wenn die GUI gestartet wird
-(void)awakeFromNib {
    // Array-Inhalt für das PopUp
    NSArray *speicherNummer = @[@"1", @"2", @"3", @"4"];
    
    // Löscht alle "Items" einträge
    [popUpSpeicherNummer removeAllItems];
    // Fügt das Array in die Lisze hinzu
    [popUpSpeicherNummer addItemsWithTitles:speicherNummer];
    // Start-Wert setzen
    [popUpSpeicherNummer selectItemAtIndex:0];
    
    // Baue Pfade für den Import und Export der Daten
    NSArray *dateiMitPfad = baueDateiMitPfad([popUpSpeicherNummer titleOfSelectedItem]);
    importDateiMitPfad = [dateiMitPfad objectAtIndex:0];
    exportDateiMitPfad = [dateiMitPfad objectAtIndex:1];
    NSLog(@"Dateiname mit Pfad für den Import: %@", importDateiMitPfad);
    NSLog(@"Dateiname mit Pfad für den Export: %@", exportDateiMitPfad);
    
    // Prüfen, ob eine Benutzerdaten-Datei vorhanden ist?
    // Benutzerdaten-Datei ist vorhanden, Daten werden in ein datenArray eingelesen.
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL fileExists = [fileManager fileExistsAtPath:exportDateiMitPfad];
    
    if (fileExists == TRUE) {
        // Import-Datei ist vorhanden, Daten werden in das Daten-Array geladen
        NSLog(@"Die Datei %@ ist vorhanden", exportDateiMitPfad);
        // Laden den Daten-Arrays von Festplatte (BenutzerDatenX.plist)
        NSData *neueDaten = [[NSData alloc] initWithContentsOfFile:exportDateiMitPfad];
        datenArray = [NSKeyedUnarchiver unarchiveObjectWithData:neueDaten];
    } else {
        // Import-Datei ist nichtvorhanden, weiter mit dem importieren von Daten
        NSLog(@"Die Datei %@ ist nicht vorhanden", exportDateiMitPfad);
    }
    
    // Anzahl der Datensätze im Daten-Array ermitteln
    NSLog(@"Das Daten-Array hat %lu Datensätze", [datenArray count]);
    
}

Soweit funktioniert das ganze, was mir aber aufgefallen ist, werden die Informationen zweimal
auf der Konsole ausgegeben?

Code:
Dateiname mit Pfad für den Import: /Users/michael/Desktop/Daten Programm/DATA1.CSV
Dateiname mit Pfad für den Export: /Users/michael/Desktop/Daten Programm/BenutzerDaten1.plist
Die Datei /Users/michael/Desktop/Daten Programm/BenutzerDaten1.plist ist vorhanden
Das Daten-Array hat 142 Datensätze
Dateiname mit Pfad für den Import: /Users/michael/Desktop/Daten Programm/DATA1.CSV
Dateiname mit Pfad für den Export: /Users/michael/Desktop/Daten Programm/BenutzerDaten1.plist
Die Datei /Users/michael/Desktop/Daten Programm/BenutzerDaten1.plist ist vorhanden
Das Daten-Array hat 142 Datensätze

Da stellt sich die Frage, ob das "awakeFromNib" mehrmals durchlaufen wird?

Vielen Dank
Michael
 
Hallo,

die Antwort ist nein.
Du instanziierst wohl das Objekt mehrfach.

Viele Grüße
 
Danach habe ich auch schon geschaut, konnte aber nichts finden?

Auch die NSLog Anweisungen sind auch nur in der "awakeFromNib" Methode enthalten.
 
setz doch einfach mal einen breakpoint im debugger auf die methode.
ich bin mir gerade nicht sicher, aber ich glaube die methode wird auch jedes mal aufgerufen, wenn die app bzw das GUI element in den vordergrund geholt wird.
 
Habe es jetzt getestet und bekam folgendes Ergebnis.
Das Programm hielt am Stoppunkt

Code:
-(void)awakeFromNib {
Es wurde noch kein Fenster geöffnet.
Weiter mit Continue und die ersten Werte wurden auf der Konsole ausgegeben, das Fenster ist noch nicht erschienen.
Scheinbar stoppte das Programm wieder an diesem Stopppunkt, weil mit der Aktion weiter durch Continue ein zweites Mal die Werte in der Konsole ausgegeben wurden.
Erst nach dem zweiten Continue wird auch das Fenster der Applikation auf dem Bildschirm angezeigt.

Ist es dann überhaupt sinnvoll, hier Variablen ein zu lesen?
Die könnten ja durch den zweimaligen Aufruf dieser Methode falsche Werte verändert werden?
 
Was ich befürchtet hatte :(
Die Tabelle wird zweimal mit Daten gefüllt, weil die "awakeFromNib" Methode zweimal aufgerufen wird.

Muss mir etwas anderes einfallen lassen??????
 
Ich habe Dir doch die richtige Antwort genannt. Da ist Dein Problem und warum ignorierst Du es?

Beim ersten Durchlauf von Deinem Breakpoint kommt das Fenster, nur wird es so schnell wieder aufgeräumt, dass Du es nicht siehst.

Ich meine es nicht böse, aber Dein Code hat nicht viel mit "Programmierung" zutun.
Du mußt die Daten von der Oberfläche trennen. Vor allem sinnvoll in Methoden auslagern.
Ein BOOL wird mit YES oder NO geprüft, nicht mit TRUE.

Warum sollte man einen Archiver benutzen und die Instanz einfrieren, statt von NSArray -writeToFile: etc.

-awakeFromNib: ist der völlig falsche Ort für Dein Vorhaben.

Kli*schee*haft bleibt nur zu sagen: "Lies etwas Einführendes", "Erlerne die Grundlagen"…

Viele Grüße
 
Vielen Dank,

ich lese schon seit Wochen Bücher. Das Problem ist, das Objective-C so viel Kram hat den man lernen muss.
Ausserdem sind so viele Bücher völliger Schrott und das wenige gute ist sowas von veraltet.

Bezüglich Deiner Antwort, konnte ich nicht wirklich entnehmen was Du meinst.
Ich glaube auch nicht, das jeder andere, der anfängt Objective-C zu lernen innerhalb von 3 Wochen alle kann.

Dann sag mir mal welcher Ort dann der richtige für mein Vorhaben wäre?

Sag jetzt nicht CoreDate, oder NSUserdefaults!

Habe aber schon eine Idee, werde das jetzt erst mal umsetzen
 
Sag jetzt nicht CoreDate, oder NSUserdefaults!
Ne, das hat damit erstmal gar nichts zutun…

Du schreibst ja irgend ein CSV-Ding.
Hier mal ein Beispiel, wie Du die Sache angehst:

- Du erzeugst eine Klasse MyDataController
- der fügst Du einen -initWithPAth: hinzu
- dann eine Methode -readData:
- diese Methode verarbeitet Deine Daten und bereitet sie für Dich brauchbar auf

- Dein WindowController bekommt die Instanz von MyDataController
- im WindowController machst Du Dir ne Methode -updateInterface:
- in Abhängigkeit der Daten von Deinem MyDataController baust Du Dir dann die Oberfläche auf
- die Methode kannst Du dann in -awakeFromNib: -init: etc. aufrufen

Derweil versuchst Du in einer Methode alles rein zu quetschen.
Deshalb brauchst Du auch diese Menge Kommentare, damit es für Dich nachvollziehbar bleibt.

Aber noch mal zu Deiner "Feststellung", dass die Methode zwei Mal aufgerufen wird.
Dem ist nicht so.

Irgendwo in Deinem anderen Quelltext erzeugst Du zwei Mal die WindowController-Instanz.

[[WindowController alloc] init]

… das wirst Du irgendwo zwei Mal ausführen und aber nur einmal gebrauchen, d.h. irgendwo mit einem -retain oder -stong halten.

Die Instanz, die Du nicht hältst wird Dir ARC wieder aufräumen.
Aber richtigerweise wurde der Code von der kurzlebigen Instanz ausgeführt.

Viele Grüße
 
Vielen Dank für die ausführliche Info.
Der aktuelle Stand meines derzeitigen Lernprojektes ist zur Zeit.

Klassen: Model
Berechnungen (Methodensammlungen für diverses)
Person (Variablen und Methoden für Personen)

Contoller: Controller
ImportController (Verbindung vom Model zum View)
Im ImportController.m schreibe ich den Code

View:
ViewController (Hier ist meine Oberfläche)

In der View habe ich ein
Popup für das Auswählen von Benutzer (1-4)
TableView für das Anzeigen der Datenwerte
Button für das importieren

Wenn ich das PopUp 1 auswähle, dann wird
Überprüft, ob es bereits eine DatenDatei des Benutzers 1 existiert.
Wenn es existiert, dann wird es geladen und in der Tabelle dargestellt.
(Das war die Initialisierung, die ich aus dem "awakeFromNib" heraus genommen habe und in das PopUp Action gesteckt habe)

Drückt man den Button Import
Dann wird die CSV Datei des Benutzers 1 eingelesen
bearbeitet und an das Daten-Array angehängt.
Das Datenarray wird dann wieder auf Festplatte gespeichert.

Soweit so gut.
Das kommt in etwa dem hin, was Du da oben aufgelistet hast.

Die vielen Kommentare die ich da schreibe sind für mich eine Wunderbare Gedächtnisstütze da ich ja noch am lernen bin und muss dann auch nicht sonderlich viel schreiben, wenn ich mal Hilfe brauche. Je mehr ich erlerne und je öfters ich Programmiere, werden die Kommentare auch weniger.
 
Da muss man Dich loben… ;-)

Soweit so gut.
Das kommt in etwa dem hin, was Du da oben aufgelistet hast.
Ja, damit hast Du das theoretisch verstanden.
Nur in Deiner -awakeFromNib: arbeitest Du in der Praxis noch dagegen.

Hast Du jetzt die Stelle im Code gefunden, bei der Du die Instanz mehrfach erzeugst?
Das ist ja derweil die Lösung für Dein aktuelles Problem, das Du beseitigen wolltest.

Die vielen Kommentare die ich da schreibe sind für mich eine Wunderbare Gedächtnisstütze
Als solches legitim und auch für den Anfang in Ordnung.
Aber ganz schnell wirst Du feststellen, dass das wenig Nutzen hat.

Den eigentlich Programmablauf kann man in Kommentare nicht unterbringen.
Die Funktionsweise des Codes ergibt sich aber sehr schnell aus dem Aufbau.
Objective-C/Cocoa und jetzt Swift sind ja "selbst erklärende" Sprachen.

D.h. [string stringByAppendingString:mad:"Hallo"] ist doch absolut klar was das macht und Bedarf keiner Erklärung.
In anderen Sprachen ist das viel "abstrakter" und nicht so "selbst redend".

Wenn Du Deine eigenen Klassen und Methoden in dieser "gesprochenen" Qualität aufbaust, dann sind Kommentare weitgehend überflüssig.
Ich glaube in meinem letzten Projekt mittlerer Größe habe ich vielleicht fünf Kommentare geschrieben. Der Rest ist einfach klar.

Mal ein Beispiel, siehe unten…
Das Bedarf keinen Kommentar und was da passiert ist klar.
Auch der Aufbau ist aus meiner Sicht hervorragend nachvollziehbar.

Viele Grüße

Code:
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
{

    // …
    
    NSLog(@"a: %@", [self importFileURL]);
    
    // …
    
    NSLog(@"b %@", [self exportFileURL]);
    
    // …
    
    [NSApp terminate:self];
    
    // …
    
}

-(NSURL*)workspaceDirectoryURL
{
    
    // …
    
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    if(fileManager == nil)
    {
    
        return nil;
    
    }
    
    // …
    
    NSArray *array = [fileManager URLsForDirectory:NSDownloadsDirectory
                                         inDomains:NSUserDomainMask];
    
    if([array count] < 1)
    {
    
        return nil;
    
    }

    // …
    
    NSURL *URL = [array objectAtIndex:0];
    
    if(URL == nil)
    {
    
        return nil;
    
    }
    
    // …
    
    return URL;
    
    // …

}

-(NSURL*)fileURLWithName:(NSString*)name
{

    // …
    
    if([name length] < 1)
    {
        
        return nil;
        
    }
    
    // …
    
    NSURL *URL = [self workspaceDirectoryURL];
    
    if(URL == nil)
    {
        
        return nil;
        
    }
    
    // …
    
    URL = [URL URLByAppendingPathComponent:name
                               isDirectory:NO];
    
    // …
    
    return URL;
    
    // …

}

-(NSURL*)importFileURL
{
    
    // …
    
    NSURL *URL = [self fileURLWithName:@"import.txt"];
    
    if(URL == nil)
    {
        
        return nil;
        
    }
    
    // …
    
    return URL;
    
    // …
    
}

-(NSURL*)exportFileURL
{
    
    // …
    
    NSURL *URL = [self fileURLWithName:@"export.txt"];
    
    if(URL == nil)
    {
        
        return nil;
        
    }
    
    // …
    
    return URL;
    
    // …
    
}
 
Vielen Dank,

alle Probleme sind derzeit behoben.

Ich bemühe mich selbstsprechenden Code zu schreiben, was auch gut zu funktionieren scheint.
Das ist alles eine Sache der Übung und ich denke in ein paar Monaten wird sich das gravierend ändern.

Jedenfalls gehört noch einiges an ausprobieren dazu und bei der Vielfalt an Möglichkeiten gibt es ja immer mehrere Wege an das Ziel zu kommen.

Das vorliegende Testprogramm macht was es soll und jetzt sichere ich es noch gegen Fehlbedienungen ab.
Dann kommen neue Funktionen hinzu wie das Filtern der Tabelle und grafische Darstellungen der Werte anhand von Diagrammen.

Viele Grüsse
Michael
 
Habe nun gedacht, endlich funktioniert es und die Daten lassen sich Problemlos laden.
Jetzt kann man aktuelle Daten nehmen.
SD-Karte rein, neues CSV rüber kopiert, Programm gestartet und alles Crashed.

Funktioniert nicht mehr ???????
Also nachdenken was kann da bloß los sein??
Mir fiel wieder ein, das ich das CSV von Hand modifiziert hatte um diese in Numbers einladen zu können.
Dabei hatte ich alle Kommata durch Semikolons ersetz und neu abgespeichert.
Also war es ziemlich einfach die entsprechende Code-Zeile ab zu ändern.

Erst war es ein echtes Problem, aber dann hat es doch noch geklappt :)
 
Zuletzt bearbeitet:
Zurück
Oben Unten