3.5 Fehlersuche
Beim Programmieren Ihrer ersten App ist wahrscheinlich der eine oder andere Fehler
aufgetreten. Dabei haben Sie schon die ersten Möglichkeiten zur Fehlersuche kennengelernt, als Xcode Sie nach der Übersetzung auf Fehler hingewiesen hat. Außerdem
hat Xcode Ihnen vielleicht Warnungen angezeigt. Der Issue Navigator (siehe Abbildung 3.50), den Sie über die Tastenkombination
+
erreichen, zeigt Ihnen alle Fehler mit roten Punkten und Warnungen mit gelben Dreiecken
zu Ihrem Code an. Sie können durch Anklicken eines Fehlers oder einer Warnung direkt
zu der entsprechenden Programmstelle gelangen.
Abbildung 3.50 Der Issue Navigator
Fehler und Warnungen unterscheiden sich darin, dass sich ein fehlerhafter Code nicht übersetzen oder gar ausführen lässt. Erhalten Sie bei der Übersetzung nur Warnungen, erzeugt Xcode trotzdem ein ausführbares Programm. Allerdings enthält dieses Programm möglicherweise Laufzeitfehler, die es ganz oder teilweise unbrauchbar machen. Sie sollten also Ihren Programmcode immer so formulieren, dass auch keine Warnungen bei der Übersetzung auftreten.
Schlagen Sie die Warnungen nicht einfach in den Wind!
Sie sollten die Warnungen auf keinen Fall ignorieren, sondern den Ursachen dafür auf den Grund gehen und diese beseitigen. Gerade als Anfänger werden Sie dabei viel lernen. Das leichtfertige Ignorieren von Warnungen oder das unbedachte Ändern von Code aufgrund von Warnungen kann fatale Folgen haben.
Wenn Sie den Menüpunkt Analyze unter Product auswählen, untersucht Xcode den Programmcode genauer. Dafür dauert jedoch die Übersetzung des Projekts länger. Xcode verwendet blaue Symbole, um Meldungen des Analyzers darzustellen. Der Analyzer findet eine Reihe von logischen Programmfehlern, die sich erst zur Laufzeit bemerkbar machen. Er kann beispielsweise einige Arten von Speicherlecks oder die Verwendung nicht initialisierter Variablen aufdecken.
Abbildung 3.51 Analyzer-Meldung in Xcode
3.5.1 Logging

Eine einfache Möglichkeit der Fehlersuche ist die Verwendung des Loggings. Mit dem Logger können Sie beliebige Texte in die Konsole schreiben. Wenn Sie Ihre
App über Xcode ausführen, können Sie die Konsole in einem Teil des Debuggerbereichs sehen. Diesen Bereich können Sie auch über das Menü View • Show Debug Area oder die Tastenkombination
+
+
öffnen.
Die Ausgabe Ihrer Apps auf einem Gerät können Sie jedoch auch über Xcode ansehen, indem Sie den Organizer über den Menüpunkt Organizer unter Window öffnen. Dort wählen Sie das Symbol Devices und unter dem gewünschten Gerät den Punkt Console aus.
Über die Funktion NSLog können Sie eine Ausgabe ins Log schreiben. Der Aufruf dieser Funktion ähnelt dem der C-Funktion printf, die Sie vielleicht kennen. NSLog erlaubt Ihnen also formatierte Ausgaben – mit dem Unterschied, dass NSLog einen Objective-C-String als ersten Parameter erwartet, für den Sie möglichst immer ein String-Literal der Form @"..." und keine Variable verwenden sollten. Der LLVM Compiler 4.0 gibt für Variablen im Formatparameter sogar eine Warnung aus.
NSLog mit Format
Sie sollten für den Formatparameter immer ein Literal verwenden, da Variablen Prozentzeichen enthalten können, die zu unerwarteten Ergebnissen oder Abstürzen führen können. Schreiben Sie also lieber NSLog(@"%@", theLogText) statt NSLog(theLogText). Bei der ersten Variante kann der Compiler den Formatparameter überprüfen und Sie bei unpassenden Platzhalter-Wert-Kombinationen (wie beispielsweise für NSLog(@"text = %d", theLogText)) warnen.
Wenn beispielsweise die Variable die Zeichenkette @"Es gibt 10 %einmalig." enthält, erwartet NSLog für den Platzhalter %e einen Fließkommaparameter. Während die erste Variante unproblematisch ist, führt die zweite hingegen zu einem Absturz oder zumindest zu einer ungewöhnlichen Ausgabe.
Hier sind einige Beispiele für Ausgaben mit NSLog:
NSLog(@"Hallo Welt");
NSLog(@"angle=%.2f", theAngle);
NSLog(@"x=%.1f, y=%.1f", thePoint.x, thePoint.y);
NSLog(@"current time=%@", [NSDate date]);
NSLog(@"frame=%@", NSStringFromCGRect(self.frame));
Listing 3.55 Ausgaben mit »NSLog«
Sie sollten beachten, dass Sie Objekte der Klasse NSString nicht über das Format %s, sondern über %@ ausgeben. Das Format %s verwenden Sie für C-Strings, also für Zeiger auf char. Das Format %@ verwenden Sie hingegen für Objekte beliebiger Klassen. Dabei verwendet NSLog für die Ausgabe die Rückgabe der Methode description des Objekts. Diese Methode stellt die Oberklasse NSObject bereit, und Unterklassen können Sie überschreiben.
Mit NSLog können Sie also zur Laufzeit beliebige Texte in die Konsole schreiben. Sie können
diese Funktion allerdings auch dazu benutzen, um sich interessante Variablenwerte
ausgeben zu lassen; sie ist somit eine einfache Möglichkeit, um die Variablen zur
Laufzeit zu überprüfen. Xcode zeigt Ihnen allerdings nicht nur die Log-Ausgaben an,
sondern die App speichert sie auch in einer Log-Datei, und Sie können über den Organizer
(
+
+
) unter Devices darauf zugreifen, wenn Sie dort den Punkt Console unter dem Gerätnamen aufrufen.
Achtung, Plaudertaschen
Sie sollten möglichst keine privaten Daten in das Log schreiben, da es sich auch bei fremden Geräten relativ einfach auslesen lässt. Ein Angreifer braucht dazu nur das Gerät an einen Computer anzuschließen und kann dann auf alle Log-Einträge zugreifen. Vermeiden Sie also die Ausgabe von Passwörtern, Konto- oder Kreditkartennummern oder anderen schützenswerten Daten ins Log.
3.5.2 Der Debugger

Obwohl Xcode schon recht viele Fehler findet, kann Ihr Programm natürlich trotzdem noch weitere Fehler, sogenannte Laufzeitfehler, enthalten. Wie der Name schon sagt, treten diese Fehler erst bei der Ausführung des Programms auf. Diese Fehler sind leider nicht immer offensichtlich und manchmal nur sehr schwer zu finden. Bei der Verwendung von Threads kann die Fehlersuche sogar zu vorzeitiger Alterung, Haarausfall und schlechtem Atem führen. Mit steigender Programmkomplexität wird das Printf-Debugging über Log-Ausgaben auch zunehmend unbequemer.
Laufzeitfehler lassen sich in der Regel am einfachsten über einen Debugger aufspüren. Damit können Sie Ihr Programm während der Ausführung anhalten, Variablen inspizieren und gegebenenfalls verändern. Sie können das Programm entweder durch einen Pause-Knopf oder durch Haltepunkte (Breakpoints) anhalten und danach die Ausführung schrittweise oder kontinuierlich fortsetzen. Außerdem lassen sich die Breakpoints an Bedingungen knüpfen, was besonders bei Schleifen und wiederholt aufgerufenen Anweisungen hilfreich ist.
Der Debugger gehört dazu
Der Debugger gehört zu den wichtigsten Entwicklungswerkzeugen. Er erleichtert Ihnen nicht nur die Fehlersuche in Ihren Programmen, sondern hilft Ihnen auch, Ihren Code besser zu verstehen. Sie sollten sich also ruhig die Zeit nehmen, sich mit dem Debugger vertraut zu machen.
Das Setzen eines Breakpoints ist einfach. Sie klicken in die graue Leiste links neben
Ihrem Quelltext. Es erscheint ein blaugrauer Pfeil, der den Breakpoint symbolisiert
(siehe Abbildung 3.52). Der Debugger stoppt dann an dieser Stelle oder an der ersten Anweisung nach dieser
Stelle, wenn Sie den Haltepunkt in eine Zeile gesetzt haben, in der keine Anweisung
steht. Durch einen Rechtsklick auf das Breakpoints-Symbol und die Auswahl des entsprechenden
Menüpunkts können Sie den Breakpoint wieder entfernen. Alternativ verwenden Sie den
Breakpoint Navigator
dazu oder ziehen den Haltepunkt wieder aus der Leiste. Außerdem erlaubt Xcode Ihnen
das Verschieben der Breakpoints in der Leiste.
Abbildung 3.52 Setzen eines Breakpoints
Über den Pop-up-Menüpunkt Edit Breakpoint lässt sich einem Breakpoint auch eine Bedingung zuweisen (siehe Abbildung 3.53). Dabei können Sie in dem Eingabefeld Condition eine beliebige, gültige Bedingung in Objective-C formulieren. Der Debugger hält erst dann an diesem Breakpoint an, wenn die Bedingung erfüllt ist.
Abbildung 3.53 Setzen einer Breakpoint-Bedingung
Wenn der Debugger anhält, zeigt er einen grünen Pfeil links neben dem Quelltext an, der die aktuelle Ausführungsposition darstellt (siehe Abbildung 3.54). Dieser Pfeil heißt auch Programmzeiger. Der Debugger hat allerdings die Anweisung an dieser Position noch nicht ausgeführt.
Abbildung 3.54 Halt des Programms an einem Breakpoint (Zeile 93)
Sie können sich während des Halts die aktuellen Variablenwerte ansehen und sie durch einen Doppelklick auf die betreffende Zeile ändern. Das funktioniert jedoch leider nur bei einfachen Variablen. Bei Zeigern auf Objekte können Sie so lediglich den Zeiger verbiegen (z. B. auf 0 also nil setzen). Außerdem haben Sie über die Steuerungsleiste des Debuggers (siehe Abbildung 3.55) folgende Möglichkeiten:
- Sie lassen die Ausführung bis zum nächsten Breakpoint oder einem manuellen Halt fortfahren. Während Ihr Programm läuft, zeigt der Debugger statt des Fortfahren- ein Pause-Symbol, mit dem Sie die Ausführung anhalten lassen können.
- Sie können auch nur die nächste Anweisung ausführen. Durch mehrfaches Anklicken dieses Symbols können Sie Ihr Programm schrittweise ausführen.
- Falls der Debugger vor einem Methoden- oder Funktionsaufruf angehalten hat, können Sie ihn in diese Methode hineinspringen lassen. Das funktioniert allerdings nur, wenn zu dieser Methode auch die entsprechenden Debugging-Symbole vorhanden sind. Das ist bei den Systembibliotheken in der Regel nicht der Fall. Ansonsten verhält sich diese Funktion wie das schrittweise Ausführen.
- Analog können Sie auch aus einer Methode hinausspringen. Der Debugger läuft dabei so lange weiter, bis er die aktuelle Methode verlässt. Das funktioniert auch, wenn die Methode aus dem Betriebssystem oder einer Programmbibliothek heraus aufgerufen wurde. Sie bekommen dann statt Objective-C-Quellcode Assembler-Anweisungen zu sehen.
- Die Steuerungsleiste zeigt Ihnen den aktuellen Thread an, in dem sich das Programm gerade befindet. Über eine Liste können Sie auch die anderen Threads auswählen und ansehen.
- Außerdem zeigt die Steuerleiste Ihnen die oberste Methode beziehungsweise Funktion des Stapels des ausgewählten Threads an. Durch Anklicken können Sie sich die anderen Methoden und Funktionen auf dem Stapel ansehen und in diese wechseln.
Die Funktionsweise des Debuggers soll jetzt anhand des Beispielprojekts verdeutlicht werden. Setzen Sie dazu einen Breakpoint in die erste Zeile der Actionmethode updateViews in der Klasse AlarmClockViewController, und starten Sie die App über Xcode. Der Simulator öffnet das Programm, und der Debugger stoppt die Ausführung in der ersten Zeile dieser Methode.
Die Variablenansicht, die sich unter der Steuerleiste des Debuggers befindet, zeigt unter anderem auch das Symbol self mit einem Dreieck zum Aufklappen an. In diesem Symbol finden Sie die Attribute des Viewcontrollers und eine Zeile mit dem Namen UIViewController. Diese Zeile repräsentiert die Daten der Oberklasse des Objekts. Sie können die Attribute und das Symbol für die Oberklasse aufklappen und sich auch dort die enthaltenen Attribute ansehen.
Klicken Sie jetzt das auf Symbol, um die nächste Anweisung auszuführen. Der grüne Pfeil springt zur nächsten Zeile. Danach klicken Sie mehrmals auf das Symbol, bis der Programmzeiger in der letzten Zeile der Methode mit der Anweisung [self updateTimeLabel]; steht. Bei der schrittweisen Ausführung des Codes ist Ihnen sicherlich aufgefallen, dass der Debugger nur einen Block der Bedingung durchlaufen hat. Welchen Block die App durchläuft, hängt natürlich vom Benachrichtigungsobjekt in der Methode ab.
Springen Sie mit dem Debugger jetzt in die Methode updateTimeLabel, indem Sie auf das Symbol zum Hineinspringen in eine Methode klicken. Der Debugger hält in der ersten Zeile der Methode updateTimeLabel. Wenn Sie das Symbol zum Verlassen der Methode anklicken, springt der grüne Pfeil in die Zeile mit der letzten schließenden, geschweiften Klammer der Methode updateViews.
Löschen Sie den Breakpoint über das Popupmenü, und setzen Sie danach einen Breakpoint in die letzte Zeile der Methode angleWithPoint: der Klasse ClockControl. Geben Sie über den Pop-up-Menüpunkt Edit Breakpoint die Bedingung theAngle < 0 ein, und führen Sie danach das Programm aus. Der Debugger sollte jetzt nur anhalten, wenn Sie den Alarmzeiger in die linke Hälfte der Uhr bewegen. Denn dann ist der Winkel, den die Funktion atan2 liefert, kleiner als 0.
3.5.3 Die Debuggerkonsole

Der Debugger in Xcode basiert auf dem Kommandozeilenprogramm gdb oder dem moderneren lldb, den Xcode ab Version 4.3 als Standarddebugger verwendet. Sie können den Debugger über das Schema (siehe Kapitel 8) explizit auswählen. Die nachfolgend aufgeführten Befehle beziehen sich alle auf den lldb.
Xcode wandelt Ihre Eingaben (z. B. zum Setzen eines Breakpoints mit der Maus) in Debuggerbefehle um. Diese grafische Steuerung des Debuggers ist zwar sehr praktisch, allerdings nicht
immer ausreichend. Über die Debuggeransicht haben Sie auch Zugriff auf die Debuggerkonsole
(siehe Abbildung 3.56), die Sie auch über
+
+
öffnen können. Mit den drei Buttons über der Ansicht können Sie einstellen, ob Sie
nur die Variablenansicht, die Debuggerkonsole oder beide Ansichten sehen möchten.
In der Debuggerkonsole finden Sie alle Log-Ausgaben Ihrer App und auch alle Ausgaben
des Debuggers. Außerdem können Sie hinter dem Prompt (lldb) Befehle für den Debugger eingeben.
Befehlseingabe
Sie können in die Debuggerkonsole nur dann Befehle eingeben, wenn Sie die Ausführung
Ihres Programms durch einen Breakpoint oder die Pausetaste angehalten haben. Die Konsole
zeigt auch nur dann eine Eingabeaufforderung (Prompt) an. Jeden Befehl schließen Sie
durch einen Zeilenvorschub (
oder
) ab, damit der Debugger den Befehl auch ausführt.
Der lldb besitzt einen sehr großen Befehlsumfang, deren vollständige Beschreibung den Rahmen dieses Buches sprengen würde, weswegen dieser Abschnitt nur die wichtigsten Befehle behandelt. Sie können jedoch sehr viele Befehle über die GUI-Einbindung in Xcode ausführen, und viele Befehle brauchen Sie auch eher selten. Glücklicherweise besitzt der Debugger zudem noch eine eingebaute Hilfe, mit der Sie schnell jeden beliebigen Befehl nachschlagen können. Sie rufen die Hilfe über das Kommando help auf, worauf lldb mit den vorhandenen Hilfekategorien antwortet.
In der Kürze liegt die Würze
Sie dürfen Debuggerkommandos abkürzen, solange der Befehl dabei eindeutig bleibt. Anstatt »help« können Sie also auch »he« oder nur »h« eingeben, und der Debugger zeigt Ihnen auch für diese Eingaben die Hilfe an.
Abbildung 3.56 Die Debuggerkonsole in Aktion
Hinter das Hilfekommando können Sie den Namen einer Kategorie oder eines Kommandos schreiben. Dann zeigt Ihnen der Debugger die Hilfe zu dieser Kategorie beziehungsweise zu diesem Kommando an. Wenn Sie beispielsweise »help help« eingeben, sehen Sie den Hilfetext zum Kommando help.
Ablaufsteuerung
Über die Debuggerkonsole steuern Sie die Ausführung Ihrer App. Nach dem Halt an einem Breakpoint können Sie das Programm über den Befehl continue oder c weiterlaufen lassen. Dieses Kommando akzeptiert eine Zahl als optionales Argument. Die Zahl gibt an, wie oft der Debugger diesen Breakpoint erreichen muss, damit er wieder daran anhält. Wenn Sie also »c 4« eingeben, dann ignoriert der Debugger dreimal diesen Breakpoint und stoppt erst beim vierten Mal.
Über das Kommando step oder kurz s weisen Sie den Debugger an, die aktuelle Anweisung auszuführen. Falls das ein Funktions- oder Methodenaufruf ist, springt der Debugger da hinein. Auch diesem Kommando können Sie über ein optionales Argument mitteilen, wie oft der Debugger es ausführen soll. Beispielsweise führt der Debugger mit dem Kommando s 4 die vier nächsten Anweisungen aus.
Analog zu step funktioniert das Kommando next oder n, außer dass es keine Subroutinen anspringt. Mit next können Sie also Methoden- oder Funktionsaufrufe einfach überspringen. Auch bei diesem Befehl können Sie wieder durch ein optionales Argument angeben, wie oft der Debugger es ausführen soll.
Die aktuelle Funktion oder Methode können Sie über den Befehl finish oder fin verlassen. Der Debugger lässt dann das Programm so lange weiterlaufen, bis es die aktuelle Methode oder Funktion beendet hat. Wenn Sie den Debugger beispielsweise an einem Breakpoint in der Klasse drawClockHands anhalten lassen und den Befehl fin eingeben, springt der Programmzeiger ans Ende der drawRect:-Methode hinter die Anweisung [self drawClockHands].
Datenausgabe
Die am häufigsten verwendeten Kommandos sind wahrscheinlich print-object und print, die Sie aufgrund ihrer häufigen Verwendung auch mit po und p abkürzen können. Beide Befehle dienen dazu, Programmdaten auszugeben, wobei Sie hinter den Befehl einen Objective-C-Ausdruck schreiben müssen.
Mit print-object können Sie ein Objective-C-Objekt ausgeben. Wenn Sie beispielsweise einen Breakpoint in die Methode viewDidLoad wie in Abbildung 3.56 setzen und nach dem Halt des Programms an dieser Stelle den Befehl po self oder alternativ print-object self eingeben, erhalten Sie ungefähr die Ausgabe <AlarmClockViewController: 0x4b828f0>. Dabei kann die hexadezimale Zahl 0x4b828f0 am Ende abweichen, weil sie die Speicheradresse des Objekts angibt, die sich bei jedem Programmlauf ändern kann. Sie können auch kompliziertere Ausdrücke verwenden und sich beispielsweise mit po [[self textLabel] text] den Wert des Labels für die Anzeige der Alarmzeit ausgeben lassen. Leider dürfen Sie hier aber nicht die Punktnotation verwenden, weil der Debugger sie nicht kennt. Der Befehl po self.textLabel.text führt zur Ausgabe There is no member named textLabel.
Für die Textausgabe eines Objekts in der Konsole muss der Debugger das Objekt in eine Zeichenkette umwandeln, sofern es nicht bereits eine ist. Dazu verwendet er die Methode description des Objekts. Sie können diese Methode also überschreiben, um die Ausgabe im Debugger anzupassen. Für die Klasse AlarmClockViewController können Sie die Methode aus Listing 3.56 verwenden. Dann gibt Ihnen der Debugger die eingestellte Alarmzeit und den Zustand des Alarmschalters aus.
- (NSString *)description {
return [NSString stringWithFormat:@"alarm: %@ (%d)", 
self.timeLabel.text, self.alarmSwitch.on];
}
Listing 3.56 Anpassung der Debuggerausgabe
Über den Befehl print können Sie einfache und zusammengesetzte Datentypen ausgeben. Allerdings müssen Sie hierbei dem Debugger unter Umständen mitteilen, welchen Typ die Ausgabe hat. Um das an einem praktischen Beispiel zu sehen, setzen Sie einen Breakpoint in die Zeile mit der vierten Anweisung [Diese Anweisung lautet: CGContextSaveGState(theContext);] in der Methode drawRect: der Klasse ClockView und starten die Ausführung. Wenn die Ausführung in dieser Zeile hält, können Sie sich den Wert der Variablen theRadius über das Kommando p theRadius anzeigen lassen. Vor dem Wert steht ein Dollarzeichen, gefolgt von einer fortlaufenden Nummer und einem Gleichheitszeichen, also beispielsweise: $1 = 160. [Die Nummer hinter dem Dollarzeichen hängt davon ab, wie oft Sie bereits den print-Befehl aufgerufen haben. Es ist also nicht schlimm, wenn in Ihrer Konsole eine andere Nummer steht.] Der Debugger merkt sich die ausgegebenen Werte in internen Variablen, die Sie sich jederzeit über print wieder ausgeben lassen können (z. B. mit p $1). Sie können diese Variablen sogar in Ausdrücken verwenden und damit rechnen (z. B. p 2 * $1 + 20).
Wert versus Ausdruck
Die Debuggervariablen speichern immer den Wert, das heißt das Ergebnis, des Ausdrucks und nicht den Ausdruck. Sie können damit also alte Variablenwerte abfragen. Wenn Sie zu einem späteren Zeitpunkt wieder p $1 eingeben, erhalten Sie immer den Variablenwert 160 – unabhängig davon, welchen Wert die Variable theRadius momentan hat.
Das klappt auch mit der Struktur theBounds und dem Befehl p theBounds. Die Ausgabe sieht auf einem iPhone so aus:
$3 = {
origin = {
x = 0,
y = 0
},
size = {
width = 320,
height = 320
}
}
Der Debugger gibt Ihnen also schön die Werte in der Struktur aus. Wenn Sie sich den Frame des Views ausgeben lassen wollen und p [self frame] eingeben, liefert Ihnen der Debugger hingegen die Fehlermeldung:
Unable to call function "objc_msgSend" at 0x14c908c: no return type information available.
To call this function anyway, you can cast the return type explicitly (e.g. 'print (float) fabs (3.0)')
Das liegt daran, dass der Debugger nicht den Rückgabetyp der Methode frame ermitteln kann. Sie können ihm jedoch mit einem Typcast auf die Sprünge helfen. Wenn Sie p (CGRect)[self frame] eingeben, erhalten Sie die gewünschte Ausgabe.
print-object oder print?
Sie sollten den print-object-Befehl nur aufrufen, wenn Sie sicher sind, dass der Ausdruck auch wirklich eine Referenz auf ein Objekt liefert. Bei einfachen Datentypen, wie beispielsweise po theRadius oder po theBounds, führt dieser Befehl zum Absturz Ihrer App. Sie müssen sie dann erst wieder neu starten, um sie weiter entwanzen zu können. Der Aufruf des print-Befehls auf eine Objektreferenz ist hingegen harmlos und wenig informativ. Sie erhalten als Ausgabe im Falle eines Zeigers auf ein Objective-C-Objekt den Zeigertyp und die Speicheradresse.
Automatische Ausgaben
Beim Debuggen tritt häufig der Fall auf, dass das Programm an einem bestimmten Breakpoint mehrmals hält und Sie sich den Wert bestimmter Variablen ansehen wollen. Beispielsweise könnten Sie jedes Mal, wenn die Ausführung in drawRect: stoppt, den Wert der Variablen theRadius sehen wollen. Natürlich können Sie nach jedem Halt p theRadius aufrufen, was allerdings auf die Dauer sehr unbequem ist. Stattdessen können Sie auch den Befehl display mit einem Ausdruck als Argument verwenden. Wenn Sie den Befehl display theRadius eingeben, antwortet Ihnen der Debugger mit 1: theRadius = 160. Jedes Mal, wenn er diesen Breakpoint erreicht, gibt er Ihnen den Wert der Variablen theRadius aus. Sie können auch mehrere automatische Ausgaben an einem Breakpoint machen. Dazu brauchen Sie nur entsprechend weitere display-Befehle absetzen. Wenn Sie mit dem Befehl display theBounds eine weitere automatische Ausgabe anlegen, gibt Ihnen der Debugger bei jedem Halt an diesem Breakpoint die Werte dieser beiden Variablen aus.
Keine automatische Ausgabe von Objekten
Es gibt leider keinen analogen Befehl zu print-object für display. Sie können sich jedoch eine Behelfslösung basteln. Der Befehl print-object ruft ja die description-Methode des Objekts auf, um das Objekt in eine Zeichenkette der Klasse NSString umzuwandeln. Die Klasse NSString hat die Methode cString, die den Inhalt der Zeichenkette char-Array liefert. Arrays vom Typ char kann der Debugger über die Befehle print oder display anzeigen; Sie können das ausnutzen, um beliebige Objekte in einer automatischen Ausgabe anzuzeigen, wie z. B. display (char *)[[self description] cString]. Anstelle von self können Sie natürlich auch jede beliebige andere Objektreferenz verwenden.
Über den Befehl target stop-hook list erhalten Sie eine Liste aller automatischen Ausgaben. Nach den zwei display-Kommandos sollten Sie also die folgende Ausgabe sehen:
Hook: 1
State: enabled
Commands:
expr -- theBounds
Hook: 2
State: enabled
Commands:
expr -- theRadius
Die erste Zeile jedes Hooks enthält die Nummer der Ausgabe und die dritte den Ausdruck, den Sie im display-Befehl angegeben haben. In der zweiten Zeile können Sie sehen, ob die Ausgabe aktiv (enabled) oder inaktiv (disabled) ist.
Über den Befehl undisplay lässt sich die automatische Ausgabe eines Ausdrucks stoppen. Wenn Sie nur eine Ausgabe löschen wollen, müssen Sie die Zahl, die am Anfang der Ausgabe steht, als Argument angeben. Sie können also die Ausgabe von theResult durch den Befehl undisplay 1 wieder unterdrücken.
Wenn Sie die Ausgabe eines Ausdrucks nur temporär unterdrücken möchten, können Sie ihn über den Befehl target stop-hook disable und die Angabe der Nummer deaktivieren. Mit dem Befehl target stop-hook disable 2 deaktivieren Sie beispielsweise die Ausgabe der Variablen theBounds. Analog erlaubt Ihnen der Befehlteil enable, eine deaktivierte Ausgabe wieder zu aktivieren. Das heißt, mit target stop-hook enable 2 schalten Sie die automatische Ausgabe dieser Variablen wieder ein.
Werte verändern
Der Debugger erlaubt jedoch nicht nur die Anzeige, sondern auch die Veränderung von Variablenwerten. Sie können damit beispielsweise ausprobieren, wie sich Ihr Code mit anderen Werten verhält. Um einen Wert in der Debuggerkonsole zu setzen, verwenden Sie den Befehl expression mit einer gewöhnlichen Zuweisung in Objective-C-Syntax. Zum Beispiel können Sie über expression theRadius = 200 den Wert der Variablen theRadius in der Methode drawRect: verändern. Das geht auch mit den Attributen in einer Struktur, wie beispielsweise expression theBounds.position.x = 10.
3.5.4 Laufzeitfehler
Viele Laufzeitfehler enden in einem Absturz des Programms. In einigen Fällen bekommen Sie einen Hinweis auf die Ursache. Der Debugger zeigt Ihnen die Stelle, die den Absturz ausgelöst hat, und in bestimmten Fällen finden Sie in der Konsole eine Fehlermeldung. Die Absturzstelle entspricht allerdings in vielen Fällen nicht der Absturzursache, und diese Stelle ist häufig auch in Systembibliotheken zu finden.
Das bedeutet jedoch nicht, dass Sie einen Bug in iOS gefunden haben. Cocoa Touch wird jeden Tag von vielen Millionen Entwicklern wie Ihnen getestet, und Sie sollten zuerst immer davon ausgehen, dass Ihr Code den Fehler verursacht hat. Natürlich ist auch iOS nicht fehlerfrei. Einen Bug in den Systembibliotheken kann hingegen nur Apple beheben, und Sie sollten vorher alle Möglichkeiten ausprobieren, um Ihre App zum Laufen zu bekommen.
Nach einem Absturz wird die Absturzstelle mit dem Absturzsignal im Quellcode angezeigt. Dieses Signal gibt häufig direkt oder indirekt einen Hinweis auf die Absturzursache.
Abbildung 3.57 Anzeige der Absturzstelle mit dem Signal (SIGABRT)
Der Absturz in Abbildung 3.57 hat das Signal SIGABRT ausgelöst. Das geschieht in der Regel, wenn das Programm eine Ausnahme ausgelöst hat. Die finden Sie in der Konsole des Debuggers. Dabei erfolgt die Ausgabe der Ausnahme in fetter Schrift und ist in zwei Teile unterteilt. Die erste, sehr lange Zeile enthält eine Beschreibung der Ausnahme, und die restlichen Zeilen enthalten den Stacktrace. Das ist die Aufrufliste der Methoden des Programms bis zur Fehlerstelle.
Die Beschreibung der Ausnahme in Abbildung 3.57 lautet:
Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[<UIViewController 0x4b44d90> setValue:forUndefinedKey:]:
this class is not key value coding-compliant for the key clockControl.'
Ihr Programm hat also versucht, bei einem Objekt zum Schlüssel clockControl einen Wert zu setzen. Die Meldung gibt auch die Klasse des Objekts (UIViewController) aus, und das ist der Hinweis auf die Ursache. In der Regel verwenden Sie Unterklassen von UIViewController und erzeugen keine Objekte dieser Klasse. Dieser Fehler tritt häufig auf, wenn eine XIB-Datei einen Viewcontroller anlegt, der Entwickler indes vergessen hat, diesem Objekt über dessen Identitätsinspektor die richtige Klasse zuzuweisen. Der erste Schritt zur Behebung dieses Fehlers ist also die Überprüfung der Klasse des entsprechenden Viewcontroller-Objekts in der XIB-Datei.
Abbildung 3.58 Ausgabe nach Programmabsturz durch eine Ausnahme
Ein anderes Signal, das Sie wahrscheinlich relativ häufig zu sehen bekommen, ist EXC_BAD_ACCESS. Dieses Signal bedeutet, dass die App auf eine unerlaubte Speicherstelle zugreifen wollte, und deutet in der Regel auf einen Dangling Pointer hin.
Hier sollten Sie zunächst über den Analyzer versuchen, den Fehler einzukreisen. Für das Aufspüren von Fehlern bei der Speicherverwaltung können Sie auch das Programm Instruments verwenden, das Kapitel 6, »Models, Layer, Animationen«, vorstellt.
Ihr Kommentar
Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.













Jetzt bestellen





