eine Ausnahme in Ziel-C / Kakao werfen

Antworten:

528

Ich benutze [NSException raise:format:]wie folgt:

[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
James
quelle
9
Ich bevorzuge diesen Weg ebenso wie den @throw([NSException exceptionWith…])Ansatz.
Sam Soffes
9
Lesen Sie unbedingt die wichtigen Vorbehalte gegen Schäden ( stackoverflow.com/questions/324284/324805#324805 )
e.James
26
Im Allgemeinen bevorzuge ich das auch, aber es gibt eine Gotcha. Könnte nur meine aktuelle Version von Xcode sein, aber die Syntax [NSException raise ...] scheint vom Parser nicht als Exit-Pfad von einer Methode erkannt zu werden, die einen Wert zurückgibt. Bei Verwendung dieser Syntax wird die Warnung "Steuerung erreicht möglicherweise das Ende der nicht ungültigen Funktion" angezeigt. Bei der Syntax @throw ([NSException exceptionWith…]) erkennt der Parser dies jedoch als Exit und zeigt die Warnung nicht an.
Mattorb
1
@mpstx Ich verwende immer die Throw-Syntax aus dem von Ihnen angegebenen Grund (was zwei Jahre später in Xcode 4.6 immer noch relevant ist und wahrscheinlich immer sein wird). Wenn die IDE erkennt, dass das Auslösen einer Ausnahme ein Funktionsausgangspunkt ist, ist dies häufig wichtig, wenn Sie Warnungen vermeiden möchten.
Mark Amery
FWIW Ich stelle fest, dass @ try / @ catch-Blöcke auch zu einem falsch-negativen Ergebnis für Warnungen "Kontrolle erreicht Ende der nicht ungültigen Funktion" führen (dh die Warnung wird nicht angezeigt, wenn sie angezeigt werden sollte)
Brian Gerstle
256

Ein Wort der Vorsicht hier. In Objective-C sollten Sie im Gegensatz zu vielen ähnlichen Sprachen generell versuchen, Ausnahmen für häufige Fehlersituationen zu vermeiden, die im normalen Betrieb auftreten können.

In der Apple-Dokumentation zu Obj-C 2.0 heißt es: "Wichtig: Ausnahmen sind in Objective-C ressourcenintensiv. Sie sollten keine Ausnahmen für die allgemeine Flusskontrolle oder einfach zur Kennzeichnung von Fehlern verwenden (z. B. wenn eine Datei nicht zugänglich ist)."

In der konzeptionellen Dokumentation zur Ausnahmebehandlung von Apple wird dasselbe erklärt, jedoch mit mehr Worten: "Wichtig: Sie sollten die Verwendung von Ausnahmen für die Programmierung oder unerwartete Laufzeitfehler wie den Zugriff auf Sammlungen außerhalb der Grenzen, Versuche, unveränderliche Objekte zu mutieren und eine ungültige Nachricht zu senden, reservieren und die Verbindung zum Fensterserver zu verlieren. Normalerweise kümmern Sie sich um diese Art von Fehlern mit Ausnahmen, wenn eine Anwendung erstellt wird, und nicht zur Laufzeit. [.....] Anstelle von Ausnahmen werden Fehlerobjekte (NSError) und die Der Kakaofehler-Übermittlungsmechanismus ist die empfohlene Methode, um erwartete Fehler in Kakaoanwendungen zu kommunizieren. "

Der Grund dafür ist zum einen die Einhaltung von Programmiersprachen in Objective-C (Verwendung von Rückgabewerten in einfachen Fällen und Referenzparametern (häufig die NSError-Klasse) in komplexeren Fällen), zum anderen, dass das Auslösen und Abfangen von Ausnahmen viel teurer ist und Schließlich (und vor allem), dass Objective-C-Ausnahmen ein dünner Wrapper um die Funktionen setjmp () und longjmp () von C sind, der Ihre sorgfältige Speicherbehandlung im Wesentlichen durcheinander bringt, siehe diese Erklärung .

schadet
quelle
11
Ich denke, dies gilt für die meisten Programmiersprachen: "Versuchen Sie, Ausnahmen für häufige Fehlersituationen zu vermeiden". Gleiches gilt für Java; Es ist eine schlechte Praxis, Benutzereingabefehler (zum Beispiel) mit Ausnahmen zu behandeln. Nicht nur wegen der Ressourcennutzung, sondern auch zur Klarheit des Codes.
Beetstra
6
Noch wichtiger ist, dass Ausnahmen in Cocoa auf nicht behebbare Programmfehler hinweisen. Andernfalls wird gegen das Framework verstoßen und kann zu undefiniertem Verhalten führen. Weitere Informationen finden Sie unter stackoverflow.com/questions/3378696/iphone-try-end-try/… .
KPM
9
"Gleiches gilt für Java." Nicht zustimmen. Sie können geprüfte Ausnahmen in Java für normale Fehlerbedingungen verwenden. Natürlich würden Sie keine Laufzeitausnahmen verwenden.
Daniel Ryan
Ich bevorzuge den Cocoa-Weg (Ausnahmen gelten nur für Programmiererfehler), daher würde ich es vorziehen , dies auch in Java zu tun, aber die Realität ist, dass Sie mit typischen Praktiken in einer Umgebung arbeiten sollten, und Ausnahmen für die Fehlerbehandlung stechen hervor wie a schlechter Geruch in Objective-C, werden aber in Java häufig für diesen Zweck verwendet.
Gnasher729
1
Dieser Kommentar beantwortet die Frage nicht. Möglicherweise möchte das OP die App nur zum Absturz bringen, um zu testen, ob das Absturzberichts-Framework wie erwartet funktioniert.
Simon
62
@throw([NSException exceptionWith…])

Xcode erkennt @throwAnweisungen als Funktionsaustrittspunkte wie returnAnweisungen. Durch die Verwendung der @throwSyntax werden fehlerhafte Warnungen vermieden , von denen Sie möglicherweise erhalten, dass die Steuerung das Ende der nicht ungültigen Funktion erreicht[NSException raise:…] .

Auch @throwkann verwendet werden , um Objekte zu werfen , die nicht der Klasse NSException sind.

Peter Hosey
quelle
11
@Steph Thirion: Weitere Informationen finden Sie unter developer.apple.com/documentation/Cocoa/Conceptual/Exceptions/… . Endeffekt? Beides funktioniert, aber @throw kann verwendet werden, um Objekte zu werfen, die nicht zur Klasse NSException gehören.
e.James
33

In Bezug auf [NSException raise:format:]. Wenn Sie einen Java-Hintergrund haben, werden Sie sich daran erinnern, dass Java zwischen Exception und RuntimeException unterscheidet. Ausnahme ist eine aktivierte Ausnahme, und RuntimeException ist deaktiviert. Insbesondere schlägt Java vor, aktivierte Ausnahmen für "normale Fehlerbedingungen" und nicht aktivierte Ausnahmen für "Laufzeitfehler, die durch einen Programmiererfehler verursacht werden" zu verwenden. Es scheint, dass Objective-C-Ausnahmen an denselben Stellen verwendet werden sollten, an denen Sie eine ungeprüfte Ausnahme verwenden würden, und Fehlercode-Rückgabewerte oder NSError-Werte werden an Stellen bevorzugt, an denen Sie eine aktivierte Ausnahme verwenden würden.

Daniel Yankowsky
quelle
1
Ja, dies ist korrekt (nach 4 Jahren: D). Erstellen Sie Ihre eigene Fehlerklasse ABCError, die sich von der NSError-Klasse erstreckt, und verwenden Sie sie für geprüfte Ausnahmen anstelle von NSExceptions. NSExceptions auslösen, wenn Programmiererfehler auftreten (unerwartete Situation, z. B. ein Problem mit dem Zahlenformat).
Chathuram
15

Ich denke, um konsequent zu sein, ist es besser, @throw mit Ihrer eigenen Klasse zu verwenden, die NSException erweitert. Dann verwenden Sie die gleichen Notationen, um endlich zu versuchen, zu fangen:

@try {
.....
}
@catch{
...
}
@finally{
...
}

Apple erklärt hier, wie Ausnahmen ausgelöst und behandelt werden: Ausnahmen abfangen Ausnahmen auslösen

rostig
quelle
Ich habe immer noch Absturz durch Laufzeitausnahme in Try Block
Famfamfam
14

Seit ObjC 2.0 sind Objective-C-Ausnahmen kein Wrapper mehr für setjmp () longjmp () von C und mit C ++ - Ausnahmen kompatibel. Das @try ist "kostenlos", aber das Auslösen und Abfangen von Ausnahmen ist weitaus teurer.

Wie auch immer, Behauptungen (unter Verwendung der Makrofamilie NSAssert und NSCAssert) lösen eine NSException aus, und das ist vernünftig, sie als Ries-Zustände zu verwenden.

Psycho
quelle
Gut zu wissen! Wir haben eine Drittanbieter-Bibliothek, die wir nicht ändern möchten und die auch bei kleinsten Fehlern Ausnahmen auslöst. Wir müssen sie an einer Stelle in der App fangen und es lässt uns nur zusammenzucken, aber dadurch fühle ich mich ein bisschen besser.
Yuri Brigance
8

Verwenden Sie NSError, um Fehler und keine Ausnahmen zu kommunizieren.

Kurze Punkte zu NSError:

  • NSError ermöglicht Fehlercodes (Ganzzahlen) im C-Stil, um die Grundursache eindeutig zu identifizieren und dem Fehlerbehandler hoffentlich zu ermöglichen, den Fehler zu überwinden. Sie können Fehlercodes aus C-Bibliotheken wie SQLite sehr einfach in NSError-Instanzen einbinden.

  • NSError hat auch den Vorteil, ein Objekt zu sein, und bietet eine Möglichkeit, den Fehler mit seinem userInfo-Wörterbuchmitglied detaillierter zu beschreiben.

  • Das Beste ist jedoch, dass NSError NICHT ausgelöst werden kann, sodass ein proaktiverer Ansatz für die Fehlerbehandlung gefördert wird, im Gegensatz zu anderen Sprachen, bei denen die heiße Kartoffel einfach immer weiter nach oben geworfen wird. An diesem Punkt kann sie nur dem Benutzer und gemeldet werden nicht auf sinnvolle Weise behandelt (nicht, wenn Sie daran glauben, OOPs größtem Grundsatz an Informationen zu folgen, die sich verstecken).

Referenzlink: Referenz

Jason Fürstenberg
quelle
Dieser Kommentar beantwortet die Frage nicht. Möglicherweise möchte das OP die App nur zum Absturz bringen, um zu testen, ob das Absturzberichts-Framework wie erwartet funktioniert.
Simon
7

So habe ich es aus "The Big Nerd Ranch Guide (4. Auflage)" gelernt:

@throw [NSException exceptionWithName:@"Something is not right exception"
                               reason:@"Can't perform this operation because of this or that"
                             userInfo:nil];
Johannes
quelle
OK, aber es sagt nicht viel über die userInfo:nil. :)
Cœur
6

Sie können zwei Methoden zum Auslösen von Ausnahmen im try catch-Block verwenden

@throw[NSException exceptionWithName];

oder die zweite Methode

NSException e;
[e raise];
Subbu
quelle
3

Ich glaube, Sie sollten niemals Ausnahmen verwenden, um den normalen Programmfluss zu steuern. Ausnahmen sollten jedoch immer dann ausgelöst werden, wenn ein Wert nicht mit einem gewünschten Wert übereinstimmt.

Wenn zum Beispiel eine Funktion einen Wert akzeptiert und dieser Wert niemals Null sein darf, ist es in Ordnung, eine Ausnahme auszulösen, anstatt zu versuchen, etwas 'Kluges' zu tun ...

Ries

R. van Twisk
quelle
0

Sie sollten nur Ausnahmen auslösen, wenn Sie sich in einer Situation befinden, die auf einen Programmierfehler hinweist, und die Ausführung der Anwendung stoppen möchten. Daher können Sie Ausnahmen am besten mit den Makros NSAssert und NSParameterAssert auslösen und sicherstellen, dass NS_BLOCK_ASSERTIONS nicht definiert ist.

gnasher729
quelle
0

Beispielcode für case: @throw ([NSException exceptionWithName: ...

- (void)parseError:(NSError *)error
       completionBlock:(void (^)(NSString *error))completionBlock {


    NSString *resultString = [NSString new];

    @try {

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];

    if(!errorData.bytes) {

        @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
    }


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&error];

    resultString = dictFromData[@"someKey"];
    ...


} @catch (NSException *exception) {

      NSLog( @"Caught Exception Name: %@", exception.name);
      NSLog( @"Caught Exception Reason: %@", exception.reason );

    resultString = exception.reason;

} @finally {

    completionBlock(resultString);
}

}}

Verwenden von:

[self parseError:error completionBlock:^(NSString *error) {
            NSLog(@"%@", error);
        }];

Ein weiterer fortgeschrittener Anwendungsfall:

- (void)parseError:(NSError *)error completionBlock:(void (^)(NSString *error))completionBlock {

NSString *resultString = [NSString new];

NSException* customNilException = [NSException exceptionWithName:@"NilException"
                                                          reason:@"object is nil"
                                                        userInfo:nil];

NSException* customNotNumberException = [NSException exceptionWithName:@"NotNumberException"
                                                                reason:@"object is not a NSNumber"
                                                              userInfo:nil];

@try {

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];

    if(!errorData.bytes) {

        @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
    }


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&error];

    NSArray * array = dictFromData[@"someArrayKey"];

    for (NSInteger i=0; i < array.count; i++) {

        id resultString = array[i];

        if (![resultString isKindOfClass:NSNumber.class]) {

            [customNotNumberException raise]; // <====== HERE is just the same as: @throw customNotNumberException;

            break;

        } else if (!resultString){

            @throw customNilException;        // <======

            break;
        }

    }

} @catch (SomeCustomException * sce) {
    // most specific type
    // handle exception ce
    //...
} @catch (CustomException * ce) {
    // most specific type
    // handle exception ce
    //...
} @catch (NSException *exception) {
    // less specific type

    // do whatever recovery is necessary at his level
    //...
    // rethrow the exception so it's handled at a higher level

    @throw (SomeCustomException * customException);

} @finally {
    // perform tasks necessary whether exception occurred or not

}

}}

Aleksandr B.
quelle
-7

Es gibt keinen Grund, Ausnahmen in Ziel C normalerweise nicht zu verwenden, auch nicht um Geschäftsregelausnahmen zu kennzeichnen. Apple kann sagen, verwenden Sie NSError, wen interessiert das? Obj C gibt es schon lange und zu einer Zeit sagten ALLE C ++ - Dokumentationen dasselbe. Der Grund, warum es keine Rolle spielt, wie teuer das Werfen und Fangen einer Ausnahme ist, ist, dass die Lebensdauer einer Ausnahme außerordentlich kurz ist und ... es eine AUSNAHME vom normalen Ablauf ist. Ich habe noch nie in meinem Leben jemanden sagen hören, Mann, diese Ausnahme hat lange gedauert, bis sie geworfen und gefangen wurde.

Es gibt auch Leute, die denken, dass Ziel C selbst zu teuer ist und stattdessen Code in C oder C ++. Wenn Sie also sagen, dass Sie immer NSError verwenden, ist dies schlecht informiert und paranoid.

Aber die Frage dieses Threads wurde noch nicht beantwortet, was der BESTE Weg ist, eine Ausnahme auszulösen. Die Möglichkeiten, NSError zurückzugeben, liegen auf der Hand.

So ist es: [NSException erhöhen: ... @throw [[NSException alloc] initWithName .... oder @throw [[MyCustomException ...?

Ich verwende die aktivierte / deaktivierte Regel hier etwas anders als oben.

Der wirkliche Unterschied zwischen dem (unter Verwendung der Java-Metapher hier) aktivierten / deaktivierten Element ist wichtig -> ob Sie sich von der Ausnahme erholen können. Und mit erholen meine ich nicht nur NICHT abstürzen.

Daher verwende ich benutzerdefinierte Ausnahmeklassen mit @throw für wiederherstellbare Ausnahmen, da ich wahrscheinlich eine App-Methode haben werde, die nach bestimmten Arten von Fehlern in mehreren @ catch-Blöcken sucht. Wenn meine App beispielsweise ein Geldautomat ist, hätte ich einen @ catch-Block für die "WithdrawalRequestExceedsBalanceException".

Ich verwende NSException: Raise für Laufzeitausnahmen, da ich keine Möglichkeit habe, die Ausnahme wiederherzustellen, außer sie auf einer höheren Ebene abzufangen und zu protokollieren. Und es macht keinen Sinn, dafür eine benutzerdefinierte Klasse zu erstellen.

Jedenfalls ist es das, was ich tue, aber wenn es einen besseren, ähnlich ausdrucksstarken Weg gibt, würde ich es auch gerne wissen. In meinem eigenen Code gebe ich, da ich vor langer Zeit aufgehört habe, C a hella zu codieren, niemals einen NSError zurück, selbst wenn mir einer von einer API übergeben wird.

gelöschter Benutzer
quelle
4
Ich würde empfehlen, zu versuchen, einen Server mit Ausnahmen als Teil des normalen Ablaufs von Fehlerfällen zu programmieren, bevor allgemeine Aussagen wie "Es gibt keinen Grund, Ausnahmen in Ziel C normalerweise nicht zu verwenden" gemacht werden. Ob Sie es glauben oder nicht, es gibt Gründe, Hochleistungsanwendungen (oder zumindest Teile von Anwendungen) in ObjC zu schreiben, und das Auslösen von Ausnahmen beeinträchtigt normalerweise die Leistung erheblich.
Jbenet
6
Es gibt in der Tat sehr gute Gründe, Ausnahmen in Kakao nicht zu verwenden. Weitere Informationen finden Sie hier in der Antwort von Bill Bumgarner: stackoverflow.com/questions/3378696/iphone-try-end-try/… . Er weiß, wovon er spricht (Hinweis: Überprüfen Sie seinen Arbeitgeber). Ausnahmen in Cocoa werden als nicht behebbare Fehler behandelt und können das System in einem instabilen Zustand belassen. NSError ist der richtige Weg, um allgemeine Fehler weiterzugeben.
Brad Larson
Ausnahmen sind außergewöhnlich . Fehler bei Geschäftsregeln sind sicherlich nicht qualifiziert. "Das Finden und Entwerfen von ausnahmelastigem Code kann zu einem anständigen Perfektionsgewinn führen." MSDN über encodinghorror.com/blog/2004/10/…
Jonathan Watmough
3
Ausnahmen können nicht aus Blöcken geworfen werden. Ausnahmen, die in einer ARC-Umgebung ausgelöst werden, können zu einem Programmleck führen. Also das Downvote.
Moszi
"Es spielt keine Rolle, wie teuer das Werfen und Abfangen einer Ausnahme ist" Ich schreibe einen Emulator, bei dem die Leistung entscheidend ist. Ich kann nicht viele teure Ausnahmen werfen.
NobodyNada