Objective-C Kurz und bündig: Ausnahmen und Fehler
German (Deutsch) translation by Katharina Grigorovich-Nevolina (you can also view the original English article)
In Objective-C gibt es zwei Arten von Fehlern, die auftreten können, während ein Programm ausgeführt wird. Unerwartete Fehler sind "schwerwiegende" Programmierfehler, die normalerweise dazu führen, dass Ihr Programm vorzeitig beendet wird. Diese werden als Ausnahmen bezeichnet, da sie eine Ausnahmebedingung in Ihrem Programm darstellen. Andererseits treten erwartete Fehler natürlich im Verlauf der Programmausführung auf und können verwendet werden, um den Erfolg einer Operation zu bestimmen. Diese werden als Fehler bezeichnet.
Sie können die Unterscheidung zwischen Ausnahmen und Fehlern auch als Unterschied in ihrer Zielgruppe betrachten. Im Allgemeinen werden Ausnahmen verwendet, um den Programmierer über einen Fehler zu informieren, während Fehler verwendet werden, um den Benutzer darüber zu informieren, dass eine angeforderte Aktion nicht abgeschlossen werden konnte.


Der Versuch, auf einen nicht vorhandenen Array-Index zuzugreifen, ist beispielsweise eine Ausnahme (ein Programmiererfehler), während das Nichtöffnen einer Datei ein Fehler ist (ein Benutzerfehler). Im ersteren Fall ist im Ablauf Ihres Programms wahrscheinlich ein Fehler aufgetreten, und es sollte wahrscheinlich bald nach der Ausnahme heruntergefahren werden. In letzterem Fall möchten Sie dem Benutzer mitteilen, dass die Datei nicht geöffnet werden konnte, und ihn möglicherweise auffordern, die Aktion erneut zu versuchen. Es gibt jedoch keinen Grund, warum Ihr Programm nach dem Fehler nicht mehr ausgeführt werden kann.
Ausnahmebehandlung
Der Hauptvorteil der Ausnahmebehandlungsfunktionen von Objective-C besteht in der Möglichkeit, die Fehlerbehandlung von der Fehlererkennung zu trennen. Wenn ein Teil des Codes auf eine Ausnahme stößt, kann er ihn zum nächsten Fehlerbehandlungsblock "werfen", der bestimmte Ausnahmen "abfangen" und entsprechend behandeln kann. Die Tatsache, dass Ausnahmen von beliebigen Orten aus ausgelöst werden können, macht es unnötig, ständig nach Erfolgs- oder Fehlermeldungen von jeder Funktion zu suchen, die an einer bestimmten Aufgabe beteiligt ist.
Die Compiler-Direktiven @try, @catch() und @finally werden verwendet, um Ausnahmen abzufangen und zu behandeln, und die Direktive @throw wird verwendet, um sie zu erkennen. Wenn Sie mit Ausnahmen in C# gearbeitet haben, sollten Ihnen diese Konstrukte zur Ausnahmebehandlung bekannt sein.
Es ist wichtig zu beachten, dass in Objective-C Ausnahmen relativ langsam sind. Infolgedessen sollte ihre Verwendung darauf beschränkt sein, schwerwiegende Programmierfehler zu erkennen - nicht für den grundlegenden Steuerungsfluss. Wenn Sie versuchen, anhand eines erwarteten Fehlers zu bestimmen, was zu tun ist (z. B. wenn eine Datei nicht geladen werden kann), lesen Sie bitte den Abschnitt Fehlerbehandlung.
Die NSException-Klasse
Ausnahmen werden als Instanzen der NSException-Klasse oder einer Unterklasse davon dargestellt. Dies ist eine bequeme Möglichkeit, alle erforderlichen Informationen zu kapseln, die mit einer Ausnahme verbunden sind. Die drei Eigenschaften, die eine Ausnahme darstellen, werden wie folgt beschrieben:
-
name- Eine Instanz vonNSString, die die Ausnahme eindeutig identifiziert. -
reason- Eine Instanz vonNSString, die eine für Menschen lesbare Beschreibung der Ausnahme enthält. -
userInfo- Eine Instanz vonNSDictionary, die anwendungsspezifische Informationen zur Ausnahme enthält.
Das Foundation-Framework definiert mehrere Konstanten, die die "Standard"-Ausnahme-Namen definieren. Mit diesen Zeichenfolgen kann überprüft werden, welche Art von Ausnahme abgefangen wurde.
Sie können auch die InitialisierungsmethodeinitWithName:reason:userInfo: verwenden, um neue Ausnahmeobjekte mit Ihren eigenen Werten zu erstellen. Benutzerdefinierte Ausnahmeobjekte können mit denselben Methoden abgefangen und ausgelöst werden, die in den nächsten Abschnitten beschrieben werden.
Ausnahmen generieren
Beginnen wir mit einem Blick auf das Standardverhalten eines Programms bei der Ausnahmebehandlung. Die objectAtIndex:-Methode von NSArray ist so definiert, dass sie eine NSRangeException (eine Unterklasse von NSException) auslöst, wenn Sie versuchen, auf einen nicht vorhandenen Index zuzugreifen. Wenn Sie also das 10te. Element eines Arrays anfordern, das nur drei Elemente enthält, haben Sie selbst eine Ausnahme, mit der Sie experimentieren können:
1 |
#import <Foundation/Foundation.h>
|
2 |
|
3 |
int main(int argc, const char * argv[]) { |
4 |
@autoreleasepool { |
5 |
|
6 |
NSArray *crew = [NSArray arrayWithObjects: |
7 |
@"Dave", |
8 |
@"Heywood", |
9 |
@"Frank", nil]; |
10 |
|
11 |
// This will throw an exception.
|
12 |
NSLog(@"%@", [crew objectAtIndex:10]); |
13 |
|
14 |
}
|
15 |
return 0; |
16 |
}
|
Wenn eine nicht erfasste Ausnahme auftritt, hält Xcode das Programm an und verweist Sie auf die Zeile, die das Problem verursacht hat.


Als Nächstes lernen wir, wie Sie Ausnahmen abfangen und verhindern, dass das Programm beendet wird.
Ausnahmen fangen
Um eine Ausnahme zu behandeln, sollte jeder Code, der zu einer Ausnahme führen kann, in einem @try-Block platziert werden. Anschließend können Sie mithilfe der Anweisung @catch() bestimmte Ausnahmen abfangen. Wenn Sie einen Housekeeping-Code ausführen müssen, können Sie ihn optional in einen @finally-Block einfügen. Das folgende Beispiel zeigt alle drei dieser Anweisungen zur Ausnahmebehandlung:
1 |
@try { |
2 |
NSLog(@"%@", [crew objectAtIndex:10]); |
3 |
}
|
4 |
@catch (NSException *exception) { |
5 |
NSLog(@"Caught an exception"); |
6 |
// We'll just silently ignore the exception.
|
7 |
}
|
8 |
@finally { |
9 |
NSLog(@"Cleaning up"); |
10 |
}
|
Dies sollte Folgendes in Ihrer Xcode-Konsole ausgeben:
1 |
Caught an exception! |
2 |
Name: NSRangeException |
3 |
Reason: *** -[__NSArrayI objectAtIndex:]: index 10 beyond bounds [0 .. 2] |
4 |
Cleaning up |
Wenn das Programm auf die Nachricht [crew objectAtIndex:10] stößt, löst es eine NSRangeException aus, die in der Anweisung @catch() abgefangen wird. Im Block @catch() wird die Ausnahme tatsächlich behandelt. In diesem Fall wird nur eine beschreibende Fehlermeldung angezeigt. In den meisten Fällen möchten Sie jedoch möglicherweise Code schreiben, um das Problem zu beheben.
Wenn im @try-Block eine Ausnahme auftritt, springt das Programm zum entsprechenden @catch()-Block. Dies bedeutet, dass Code nach dem Auftreten der Ausnahme nicht ausgeführt wird. Dies stellt ein Problem dar, wenn der @try-Block bereinigt werden muss (z. B. wenn er eine Datei geöffnet hat, muss diese Datei geschlossen werden). Der @finally-Block löst dieses Problem, da er garantiert ausgeführt wird, unabhängig davon, ob eine Ausnahme aufgetreten ist. Dies macht es zum perfekten Ort, um lose Enden aus dem @try-Block zu binden.
In den Klammern nach der Anweisung @catch() können Sie definieren, welche Art von Ausnahme Sie abfangen möchten. In diesem Fall handelt es sich um eine NSException, die die Standardausnahmeklasse darstellt. Eine Ausnahme kann jedoch tatsächlich jede Klasse sein - nicht nur eine NSException. Die folgende @catch()- Direktive behandelt beispielsweise ein generisches Objekt:
1 |
@catch (id genericException) |
Im nächsten Abschnitt erfahren Sie, wie Sie Instanzen von NSException sowie generische Objekte auslösen.
Ausnahmen werfen
Wenn Sie in Ihrem Code eine Ausnahmebedingung feststellen, erstellen Sie eine Instanz von NSException und füllen sie mit den relevanten Informationen. Dann werfen Sie es mit der treffend benannten @throw-Direktive und fordern den nächsten @try/@catch-Block auf, damit umzugehen.
Das folgende Beispiel definiert beispielsweise eine Funktion zum Generieren von Zufallszahlen zwischen einem bestimmten Intervall. Wenn der Aufrufer ein ungültiges Intervall überschreitet, gibt die Funktion einen benutzerdefinierten Fehler aus.
1 |
#import <Foundation/Foundation.h>
|
2 |
|
3 |
int generateRandomInteger(int minimum, int maximum) { |
4 |
if (minimum >= maximum) { |
5 |
// Create the exception.
|
6 |
NSException *exception = [NSException |
7 |
exceptionWithName:@"RandomNumberIntervalException" |
8 |
reason:@"*** generateRandomInteger(): " |
9 |
"maximum parameter not greater than minimum parameter"
|
10 |
userInfo:nil]; |
11 |
|
12 |
// Throw the exception.
|
13 |
@throw exception; |
14 |
}
|
15 |
// Return a random integer.
|
16 |
return arc4random_uniform((maximum - minimum) + 1) + minimum; |
17 |
}
|
18 |
|
19 |
int main(int argc, const char * argv[]) { |
20 |
@autoreleasepool { |
21 |
|
22 |
int result = 0; |
23 |
@try { |
24 |
result = generateRandomInteger(0, 10); |
25 |
}
|
26 |
@catch (NSException *exception) { |
27 |
NSLog(@"Problem!!! Caught exception: %@", [exception name]); |
28 |
}
|
29 |
|
30 |
NSLog(@"Random Number: %i", result); |
31 |
|
32 |
}
|
33 |
return 0; |
34 |
}
|
Da dieser Code ein gültiges Intervall (0, 10) an generateRandomInteger() übergibt, muss keine Ausnahme abgefangen werden. Wenn Sie jedoch das Intervall auf (0,-10) ändern, wird der Block @catch() in Aktion angezeigt. Dies ist im Wesentlichen das, was unter der Haube passiert, wenn die Framework-Klassen auf Ausnahmen stoßen (z. B. die von NSArray ausgelöste NSRangeException).
Es ist auch möglich, Ausnahmen, die Sie bereits abgefangen haben, erneut auszulösen. Dies ist nützlich, wenn Sie darüber informiert werden möchten, dass eine bestimmte Ausnahme aufgetreten ist, diese aber nicht unbedingt selbst behandeln möchten. Der Einfachheit halber können Sie das Argument der @throw-Direktive sogar weglassen:
1 |
@try { |
2 |
result = generateRandomInteger(0, -10); |
3 |
}
|
4 |
@catch (NSException *exception) { |
5 |
NSLog(@"Problem!!! Caught exception: %@", [exception name]); |
6 |
|
7 |
// Re-throw the current exception.
|
8 |
@throw
|
9 |
}
|
Dadurch wird die abgefangene Ausnahme an den nächsthöheren Handler übergeben, in diesem Fall den Ausnahmebehandler der obersten Ebene. Dies sollte die Ausgabe unseres @catch()-Blocks sowie die Standard-Terminating app due to uncaught exception... anzeigen, gefolgt von einem abrupten Beenden.
Die @throw-Direktive ist nicht auf NSException-Objekte beschränkt, sondern kann buchstäblich jedes Objekt auslösen. Im folgenden Beispiel wird anstelle einer normalen Ausnahme ein NSNumber-Objekt ausgelöst. Beachten Sie auch, wie Sie verschiedene Objekte als Ziel festlegen können, indem Sie nach dem @try-Block mehrere @catch()-Anweisungen hinzufügen:
1 |
#import <Foundation/Foundation.h>
|
2 |
|
3 |
int generateRandomInteger(int minimum, int maximum) { |
4 |
if (minimum >= maximum) { |
5 |
// Generate a number using "default" interval.
|
6 |
NSNumber *guess = [NSNumber |
7 |
numberWithInt:generateRandomInteger(0, 10)]; |
8 |
|
9 |
// Throw the number.
|
10 |
@throw guess; |
11 |
}
|
12 |
// Return a random integer.
|
13 |
return arc4random_uniform((maximum - minimum) + 1) + minimum; |
14 |
}
|
15 |
|
16 |
int main(int argc, const char * argv[]) { |
17 |
@autoreleasepool { |
18 |
|
19 |
int result = 0; |
20 |
@try { |
21 |
result = generateRandomInteger(30, 10); |
22 |
}
|
23 |
@catch (NSNumber *guess) { |
24 |
NSLog(@"Warning: Used default interval"); |
25 |
result = [guess intValue]; |
26 |
}
|
27 |
@catch (NSException *exception) { |
28 |
NSLog(@"Problem!!! Caught exception: %@", [exception name]); |
29 |
}
|
30 |
|
31 |
NSLog(@"Random Number: %i", result); |
32 |
|
33 |
}
|
34 |
return 0; |
35 |
}
|
Anstatt ein NSException-Objekt auszulösen, versucht generateRandomInteger(), eine neue Zahl zwischen einigen "Standard"-Grenzen zu generieren. Das Beispiel zeigt Ihnen, wie @throw mit verschiedenen Objekttypen arbeiten kann. Genau genommen ist dies jedoch weder das beste Anwendungsdesign noch die effizienteste Verwendung der Tools zur Ausnahmebehandlung von Objective-C. Wenn Sie wirklich nur vorhatten, den ausgelösten Wert wie im vorherigen Code zu verwenden, ist es besser, eine einfache alte bedingte Prüfung mit NSError durchzuführen, wie im nächsten Abschnitt erläutert.
Darüber hinaus erwarten einige der Kern-Frameworks von Apple, dass ein NSException-Objekt ausgelöst wird. Seien Sie daher bei der Integration in die Standardbibliotheken vorsichtig mit benutzerdefinierten Objekten.
Fehlerbehandlung
Während Ausnahmen Programmierer darüber informieren sollen, wenn ein schwerwiegender Fehler aufgetreten ist, sind Fehler eine effiziente und unkomplizierte Methode, um zu überprüfen, ob eine Aktion erfolgreich war oder nicht. Im Gegensatz zu Ausnahmen sind Fehler so konzipiert, dass sie in Ihren täglichen Kontrollflussanweisungen verwendet werden.
Die NSError-Klasse
Das einzige, was Fehler und Ausnahmen gemeinsam haben, ist, dass beide als Objekte implementiert sind. Die NSError-Klasse kapselt alle erforderlichen Informationen zur Darstellung von Fehlern:
-
code- EineNSInteger, die die eindeutige Kennung des Fehlers darstellt. -
domain- Eine Instanz vonNSString, die die Domäne für den Fehler definiert (ausführlicher im nächsten Abschnitt beschrieben). -
userInfo- Eine Instanz vonNSDictionary, die anwendungsspezifische Informationen zum Fehler enthält. Dies wird normalerweise viel häufiger verwendet als dasuserInfo-Wörterbuch vonNSException.
Zusätzlich zu diesen Kernattributen speichert NSError auch mehrere Werte, die das Rendern und Verarbeiten von Fehlern unterstützen sollen. All dies sind Verknüpfungen zum in der vorherigen Liste beschriebenen userInfo-Wörterbuch.
-
localizedDescription- EinNSStringmit der vollständigen Beschreibung des Fehlers, die normalerweise den Grund für den Fehler enthält. Dieser Wert wird dem Benutzer normalerweise in einem Warnfeld angezeigt. -
localizedFailureReason- EinNSString, der eine eigenständige Beschreibung des Grundes für den Fehler enthält. Dies wird nur von Clients verwendet, die den Grund für den Fehler aus der vollständigen Beschreibung isolieren möchten. -
recoverySuggestion- EinNSString, der den Benutzer anweist, wie er den Fehler beheben kann. -
localizedRecoveryOptions- EinNSArrayvon Titeln, die für die Schaltflächen des Fehlerdialogs verwendet werden. Wenn dieses Array leer ist, wird eine einzelne OK-Schaltfläche angezeigt, um die Warnung zu schließen. -
helpAnchor- EinNSString, der angezeigt wird, wenn der Benutzer die Hilfe-Ankertaste in einem Warnfeld drückt.
Wie bei NSException kann die Methode initWithDomain:code:userInfo verwendet werden, um benutzerdefinierte NSError-Instanzen zu initialisieren.
Fehlerdomänen
Eine Fehlerdomäne ist wie ein Namespace für Fehlercodes. Codes sollten innerhalb einer einzelnen Domäne eindeutig sein, können sich jedoch mit Codes aus anderen Domänen überschneiden. Domänen verhindern nicht nur Codekollisionen, sondern geben auch Auskunft darüber, woher der Fehler stammt. Die vier wichtigsten integrierten Fehlerdomänen sind: NSMachErrorDomain, NSPOSIXErrorDomain, NSOSStatusErrorDomain und NSCocoaErrorDomain. Die NSCocoaErrorDomain enthält die Fehlercodes für viele der Standard-Objective-C-Frameworks von Apple. Es gibt jedoch einige Frameworks, die ihre eigenen Domänen definieren (z. B. NSXMLParserErrorDomain).
Wenn Sie benutzerdefinierte Fehlercodes für Ihre Bibliotheken und Anwendungen erstellen müssen, sollten Sie diese immer zu Ihrer eigenen Fehlerdomäne hinzufügen. Erweitern Sie niemals eine der integrierten Domänen. Das Erstellen einer eigenen Domain ist eine relativ triviale Aufgabe. Da Domänen nur Zeichenfolgen sind, müssen Sie lediglich eine Zeichenfolgenkonstante definieren, die mit keiner der anderen Fehlerdomänen in Ihrer Anwendung in Konflikt steht. Apple schlägt vor, dass Domains die Form com.<company>.<project>.ErrorDomain haben.
Fehler erfassen
Es gibt keine dedizierten Sprachkonstrukte für die Behandlung von NSError-Instanzen (obwohl mehrere integrierte Klassen dafür ausgelegt sind). Sie sind für die Verwendung in Verbindung mit speziell entwickelten Funktionen konzipiert, die ein Objekt zurückgeben, wenn sie erfolgreich sind, und nil, wenn sie fehlschlagen. Das allgemeine Verfahren zum Erfassen von Fehlern lautet wie folgt:
- Deklarieren Sie eine
NSError-Variable. Sie müssen es nicht zuweisen oder initialisieren. - Übergeben Sie diese Variable als Doppelzeiger an eine Funktion, die zu einem Fehler führen kann. Wenn etwas schief geht, verwendet die Funktion diese Referenz, um Informationen über den Fehler aufzuzeichnen.
- Überprüfen Sie den Rückgabewert dieser Funktion auf Erfolg oder Misserfolg. Wenn der Vorgang fehlgeschlagen ist, können Sie den Fehler mit
NSErrorselbst behandeln oder dem Benutzer anzeigen.
Wie Sie sehen können, gibt eine Funktion normalerweise kein NSError-Objekt zurück - sie gibt den Wert zurück, den sie haben soll, wenn sie erfolgreich ist, andernfalls gibt sie nil zurück. Sie sollten immer den Rückgabewert einer Funktion verwenden, um Fehler zu erkennen. Verwenden Sie niemals das Vorhandensein oder Fehlen eines NSError-Objekts, um zu überprüfen, ob eine Aktion erfolgreich war. Fehlerobjekte sollen nur einen möglichen Fehler beschreiben und nicht sagen, ob einer aufgetreten ist.
Das folgende Beispiel zeigt einen realistischen Anwendungsfall für NSError. Es verwendet eine Methode zum Laden von Dateien von NSString, die tatsächlich außerhalb des Geltungsbereichs des Buches liegt. Das iOS-Kurz und bündig behandelt die Dateiverwaltung ausführlich. Im Moment konzentrieren wir uns jedoch nur auf die Fehlerbehandlungsfunktionen von Objective-C.
Zuerst generieren wir einen Dateipfad, der auf ~/Desktop/SomeContent.txt verweist. Anschließend erstellen wir eine NSError-Referenz und übergeben sie an die Methode stringWithContentsOfFile:encoding:error:, um Informationen zu Fehlern zu erfassen, die beim Laden der Datei auftreten. Beachten Sie, dass wir einen Verweis auf den *error-Zeiger übergeben, was bedeutet, dass die Methode einen Zeiger auf einen Zeiger (d. h. einen Doppelzeiger) anfordert. Auf diese Weise kann die Methode die Variable mit ihrem eigenen Inhalt füllen. Schließlich überprüfen wir den Rückgabewert (nicht das Vorhandensein der error-Variablen), um festzustellen, ob stringWithContentsOfFile:encoding:error: erfolgreich war oder nicht. In diesem Fall können Sie sicher mit dem in der content-Variablen gespeicherten Wert arbeiten. Andernfalls verwenden wir die error-Variable, um Informationen darüber anzuzeigen, was schief gelaufen ist.
1 |
#import <Foundation/Foundation.h>
|
2 |
|
3 |
int main(int argc, const char * argv[]) { |
4 |
@autoreleasepool { |
5 |
|
6 |
// Generate the desired file path.
|
7 |
NSString *filename = @"SomeContent.txt"; |
8 |
NSArray *paths = NSSearchPathForDirectoriesInDomains( |
9 |
NSDesktopDirectory, NSUserDomainMask, YES |
10 |
);
|
11 |
NSString *desktopDir = [paths objectAtIndex:0]; |
12 |
NSString *path = [desktopDir |
13 |
stringByAppendingPathComponent:filename]; |
14 |
|
15 |
// Try to load the file.
|
16 |
NSError *error; |
17 |
NSString *content = [NSString stringWithContentsOfFile:path |
18 |
encoding:NSUTF8StringEncoding |
19 |
error:&error]; |
20 |
|
21 |
// Check if it worked.
|
22 |
if (content == nil) { |
23 |
// Some kind of error occurred.
|
24 |
NSLog(@"Error loading file %@!", path); |
25 |
NSLog(@"Description: %@", [error localizedDescription]); |
26 |
NSLog(@"Reason: %@", [error localizedFailureReason]); |
27 |
} else { |
28 |
// Content loaded successfully.
|
29 |
NSLog(@"Content loaded!"); |
30 |
NSLog(@"%@", content); |
31 |
}
|
32 |
}
|
33 |
return 0; |
34 |
}
|
Da die Datei ~/Desktop/SomeContent.txt auf Ihrem Computer wahrscheinlich nicht vorhanden ist, führt dieser Code höchstwahrscheinlich zu einem Fehler. Alles, was Sie tun müssen, um das Laden erfolgreich zu gestalten, ist, SomeContent.txt auf Ihrem Desktop zu erstellen.
Benutzerdefinierte Fehler
Benutzerdefinierte Fehler können konfiguriert werden, indem ein Doppelzeiger auf ein NSError-Objekt akzeptiert und selbst ausgefüllt wird. Denken Sie daran, dass Ihre Funktion oder Methode entweder ein Objekt oder nil zurückgeben sollte, je nachdem, ob es erfolgreich ist oder fehlschlägt (geben Sie die NSError-Referenz nicht zurück).
Im nächsten Beispiel wird anstelle einer Ausnahme ein Fehler verwendet, um ungültige Parameter in der Funktion generateRandomInteger() zu verringern. Beachten Sie, dass **error ein Doppelzeiger ist, mit dem wir die zugrunde liegende Variable innerhalb der Funktion füllen können. Es ist sehr wichtig zu überprüfen, ob der Benutzer tatsächlich einen gültigen **error-Parameter mit if (error!= NULL) übergeben hat. Sie sollten dies immer in Ihren eigenen fehlererzeugenden Funktionen tun. Da der Parameter **error ein Doppelzeiger ist, können wir der zugrunde liegenden Variablen über *error einen Wert zuweisen. Und wieder prüfen wir den Rückgabewert (if (result == nil)) und nicht die error-Variable auf Fehler.
1 |
#import <Foundation/Foundation.h>
|
2 |
|
3 |
NSNumber *generateRandomInteger(int minimum, int maximum, NSError **error) { |
4 |
if (minimum >= maximum) { |
5 |
if (error != NULL) { |
6 |
|
7 |
// Create the error.
|
8 |
NSString *domain = @"com.MyCompany.RandomProject.ErrorDomain"; |
9 |
int errorCode = 4; |
10 |
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; |
11 |
[userInfo setObject:@"Maximum parameter is not greater than minimum parameter" |
12 |
forKey:NSLocalizedDescriptionKey]; |
13 |
|
14 |
// Populate the error reference.
|
15 |
*error = [[NSError alloc] initWithDomain:domain |
16 |
code:errorCode |
17 |
userInfo:userInfo]; |
18 |
}
|
19 |
return nil; |
20 |
}
|
21 |
// Return a random integer.
|
22 |
return [NSNumber |
23 |
numberWithInt:arc4random_uniform((maximum - minimum) + 1) + minimum]; |
24 |
}
|
25 |
|
26 |
int main(int argc, const char * argv[]) { |
27 |
@autoreleasepool { |
28 |
|
29 |
NSError *error; |
30 |
NSNumber *result = generateRandomInteger(0, -10, &error); |
31 |
|
32 |
if (result == nil) { |
33 |
// Check to see what went wrong.
|
34 |
NSLog(@"An error occurred!"); |
35 |
NSLog(@"Domain: %@ Code: %li", [error domain], [error code]); |
36 |
NSLog(@"Description: %@", [error localizedDescription]); |
37 |
} else { |
38 |
// Safe to use the returned value.
|
39 |
NSLog(@"Random Number: %i", [result intValue]); |
40 |
}
|
41 |
|
42 |
}
|
43 |
return 0; |
44 |
}
|
Alle localizedDescription, localizedFailureReason und verwandten Eigenschaften von NSError werden tatsächlich in seinem userInfo-Wörterbuch mit speziellen Schlüsseln gespeichert, die durch NSLocalizedDescriptionKey, NSLocalizedFailureReasonErrorKey usw. definiert sind. Um den Fehler zu beschreiben, müssen wir lediglich einige Zeichenfolgen zu den entsprechenden Schlüsseln hinzufügen. wie im letzten Beispiel gezeigt.
In der Regel möchten Sie Konstanten für benutzerdefinierte Fehlerdomänen und Codes definieren, damit diese klassenübergreifend konsistent sind.
Zusammenfassung
In diesem Kapitel wurden die Unterschiede zwischen Ausnahmen und Fehlern ausführlich erläutert. Ausnahmen sollen Programmierer über schwerwiegende Probleme in ihrem Programm informieren, während Fehler eine fehlgeschlagene Benutzeraktion darstellen. Im Allgemeinen sollte eine produktionsbereite Anwendung keine Ausnahmen auslösen, außer im Fall wirklich außergewöhnlicher Umstände (z. B. wenn in einem Gerät nicht genügend Speicher vorhanden ist).
Wir haben die grundlegende Verwendung von NSError behandelt. Beachten Sie jedoch, dass es mehrere integrierte Klassen gibt, die sich der Verarbeitung und Anzeige von Fehlern widmen. Leider sind dies alles grafische Komponenten und damit außerhalb des Rahmens dieses Buches. In der Fortsetzung von iOS Kurz und bündig gibt es einen eigenen Abschnitt zur Anzeige und Behebung von Fehlern.
Im letzten Kapitel von Objective-C Kurz und bündig werden wir kurz und bündig eines der verwirrenderen Themen in Objective-C diskutieren. Wir werden herausfinden, wie wir mit Blöcken die Funktionalität genauso behandeln können wie mit Daten. Dies hat weitreichende Auswirkungen auf die Möglichkeiten einer Objective-C-Anwendung.
Diese Lektion ist ein Kapitel aus Objective-C Kurz und bündig, einem kostenlosen eBook des Syncfusion-Teams.



