2010-11-30 3 views
8

Einer meiner Freunde fragte mich, NSException in iPhone Apps nicht zu verwenden. Der Grund, den er gab, war "Leistungsengpass". Aber ich bin davon nicht überzeugt.Verwendung von NSException in iPhone Apps

Kann mir jemand bestätigen, dass wir die Verwendung von NSException in der iPhone App zurückstellen sollten? Wenn Sie Best Practices für die Verwendung von NSException haben, geben Sie dies bitte ebenfalls an.

UPDATE:

Diese link fordert uns Ausnahmebehandlung bei der App-Ebene zu verwenden. Hat es jemand jemals getan? Bitte geben Sie die Vorteile und andere Performance-Probleme, die es schaffen könnte.

Antwort

30

Kurz:

Keine Ausnahmen, etwas zu zeigen, verwenden aber nicht behebbare Fehler

es nur angemessen zu verwenden @ versuchen/@ Fang mit nicht behebbaren Fehler zu behandeln. Es ist nie angebracht, @ throw/@ try/@ catch zu verwenden, um Vorgänge wie unter iOS oder Mac OS X auszuführen. Achten Sie auch dann sorgfältig darauf, ob Sie eine Ausnahme verwenden, um einen nicht behebbaren Fehler anzuzeigen oder einfach abzustürzen (Aufruf abbrechen()); Ein Absturz hinterlässt oft signifikant mehr Beweise.

Zum Beispiel wäre es nicht angebracht, Outbound-Exceptions zu erfassen, außer wenn es Ihr Ziel ist, sie zu fangen und den Fehler irgendwie zu melden, dann - normalerweise - abstürzt oder zumindest warnt Benutzer, dass sich Ihre App in einem inkonsistenten Zustand befindet und Daten verloren gehen können.

Das Verhalten einer durch System-Framework-Code ausgelösten Ausnahme ist nicht definiert.


Können Sie erklären, das "Verhalten eines Ausnahme durch das System geworfen Framework-Code ist nicht definiert." in detail?

Sicher.

Die System-Frameworks verwenden ein Design, bei dem jede Ausnahme als fataler, nicht behebbarer Fehler betrachtet wird. ein Programmierfehler, für alle Absichten und Zwecke. Es gibt eine sehr begrenzte Anzahl von Ausnahmen (heh) zu dieser Regel.

Daher stellen die Systemframeworks in ihrer Implementierung nicht sicher, dass alles ordnungsgemäß aufgeräumt wird, wenn eine Ausnahme ausgelöst wird, die den System-Framework-Code durchläuft. Sine die Ausnahme ist per Definition nicht wiederherstellbar, warum die Kosten für die Bereinigung bezahlen?

Betrachten Sie diese Call-Stack:

your-code-1() 
    system-code() 
     your-code-2() 

D.h. Code, in dem Ihr Code in Systemcode aufruft, der mehr Code verwendet (ein sehr häufiges Muster, obwohl die Callstacks offensichtlich deutlich tiefer sind).

Wenn your-code-2 eine Ausnahme auslöst, die die Ausnahmen über system-code übergeben, bedeutet dies, dass das Verhalten nicht definiert ist; system-code kann oder darf Ihre Anwendung nicht in einem undefinierten, potenziell unkontrollierbaren oder datenverlustbehafteten Zustand verlassen.

Oder, stärker: Sie können keine Ausnahme in your-code-2 mit der Erwartung werfen, dass Sie es in your-code-1 fangen und behandeln können.

+0

Können Sie erklären, dass "das Verhalten einer Ausnahme, die durch den System-Framework-Code ausgelöst wird, nicht definiert ist." im Detail? – Krishnan

+0

Aktualisiert die Frage .... – Krishnan

+0

+1, Es ist schön one.Can Sie bitte sagen Sie mir meine Annahme in Bezug auf die Ausnahme ist nicht korrekt.Wenn ich NSException im Falle von bestimmten Ausnahme (Array außerhalb der Grenze) .dann es lädt alle Klassen für die Überprüfung der Ausnahme. Bitte antworten Sie. – Ishu

0

Im Allgemeinen fragen Sie sich, wenn Sie versuchen, einen Fehler zu signalisieren, oder haben Sie tatsächlich eine außergewöhnliche Bedingung? Wenn der erste, dann ist es eine sehr schlechte Idee, unabhängig von einem Leistungsproblem, real oder wahrgenommen. Wenn Letzteres, ist es definitiv das Richtige zu tun.

+2

Nicht wirklich; Ausnahmen in iOS sind für nicht behebbare Fehler. – bbum

+1

Für mich sind wiederherstellbare Situationen Fehler :) – jer

6

Ich habe Ausnahmebehandlung für meine ziemlich intensive audio app ohne irgendwelche Probleme verwendet. Nach viel Lektüre und ein bisschen Benchmarking und Disassembly-Analyse bin ich zu der kontroversen Schlussfolgerung gekommen, dass es keinen wirklichen Grund gibt, sie (intelligent) nicht zu verwenden (NSError pointer-pointers, endless conditionals ...) juck!). Das meiste, was die Leute in den Foren sagen, ist nur die Apple Docs zu wiederholen.

Ich gehe in ziemlich viel Detail in this blog post aber ich werde meine Ergebnisse hier skizzieren:

Mythos 1: @ versuchen/@ catch/@ schließlich zu teuer ist (in Bezug auf CPU)

Auf meinem iPhone 4 dauert das Werfen und Fangen von 1 Million Ausnahmen etwa 8,5 Sekunden. Dies entspricht jeweils nur etwa 8,5 Mikrosekunden. Teuer in Ihrem Echtzeit-CoreAudio-Thread? Vielleicht ein bisschen (aber Sie würden niemals Ausnahmen dort hinwerfen?), Aber eine 8,5μs Verzögerung im UIAlert, die dem Benutzer mitteilt, dass es ein Problem beim Öffnen der Datei gab, wird es bemerkt werden?

Mythos 2: @try Blöcke eine Kosten auf 32-Bit-iOS

Die Apple-Docs „auf 64-Bit-Null-Kosten @try Blöcke“ von sprechen und erklären, dass 32-Bit eine Kosten entstehen. Eine kleine Benchmarking- und Disassemblierungsanalyse scheint darauf hinzuweisen, dass es auch bei 32bit iOS (ARM-Prozessor) kostensparende @try-Blöcke gibt. Wollte Apple sagen 32bit Intel?

Mythos 3: es wichtig ist, dass Geworfen Ausnahmen Durch Cocoa-Frameworks sind nicht definiert

Ja, sie sind „undefiniert“, aber was tun Sie über den Apple Rahmen zu werfen überhaupt? Natürlich Apple behandelt sie nicht für Sie. Der ganze Punkt der Implementierung der Ausnahmebehandlung für behebbare Fehler ist es, sie lokal zu behandeln - nur nicht jede einzelne Zeile "lokal".

Ein Rand Fall ist hier mit Methoden wie NSObject:performSelectorOnMainThread:waitUntilDone:. Wenn der spätere Parameter JA ist, verhält sich das wie eine synchrone Funktion. In diesem Fall kann es passieren, dass Sie erwarten, dass die Ausnahme in Ihren aufrufenden Bereich übergeht. Zum Beispiel:

///////////////////////////////////////////////////////////////////////// 
#pragma mark - l5CCThread 
///////////////////////////////////////////////////////////////////////// 

@interface l5CCThread : NSThread @end 

@implementation l5CCThread 

- (void)main 
{ 
    @try { 

     [self performSelectorOnMainThread:@selector(_throwsAnException) withObject:nil waitUntilDone:YES]; 

    } @catch (NSException *e) { 
     NSLog(@"Exception caught!"); 
    } 
} 
- (void)_throwsAnException { @throw [NSException exceptionWithName:@"Exception" reason:@"" userInfo:nil]; } 

@end 

///////////////////////////////////////////////////////////////////////// 
#pragma mark - l5CCAppDelegate 
///////////////////////////////////////////////////////////////////////// 

@implementation l5CCAppDelegate 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    l5CCThread *thd = [[l5CCThread alloc] init]; 
    [thd start]; 

    return YES; 
} 
// ... 

In diesem Fall wird die Ausnahme wäre „durch den Kakao Rahmen“ (der Haupt-Thread der Laufschleife) fehlt Ihren Fang und Absturz passieren. Sie können dies problemlos umgehen, indem Sie dispatch_synch von GCD verwenden und das Methodenargument sowie jede Ausnahmebehandlung in sein Blockargument einfügen.

Warum verwenden NSException vorbei NSError der

Wer jemals Arbeit in einer der älteren C-basierten Frameworks wie Core Audio weiß, was eine lästige Pflicht es ist die Überprüfung, Handhabung und Fehler gemacht wird berichten. Der Hauptvorteil von @ try/@ catch und NSExceptions ist, dass der Code sauberer und einfacher zu pflegen ist.

Angenommen, Sie haben 5 Zeilen Code, die an einer Datei arbeiten. Jeder kann einen von, sagen wir, 3 verschiedenen Fehlern auslösen (z. B. nicht mehr genügend Speicherplatz, Lesefehler usw.). Anstatt jede Zeile in eine Bedingung einzufügen, die nach einem NEIN-Rückgabewert sucht und dann die NSError-Zeigeruntersuchung an eine andere ObjC-Methode auslagert (oder schlimmer noch, verwendet ein #define Makro!), Wickeln Sie alle 5 Zeilen in eine einzige @try und Behandle jeden Fehler genau dort. Denken Sie an die Zeilen, die Sie speichern werden!

Durch das Erstellen von NSException-Unterklassen können Sie auch Fehlermeldungen problemlos zentralisieren und vermeiden, dass Ihr Code mit ihnen in Konflikt gerät. Sie können die "nicht-fatalen" Ausnahmen Ihrer Anwendung auch leicht von schwerwiegenden Programmierfehlern (wie NSAssert) unterscheiden. Sie können auch die Notwendigkeit für die "Name" -Konstante vermeiden (der Name der Unterklasse ist der "Name").

Beispiele für all dies und mehr Details zu dem Benchmarks und Demontage sind on this blog post ...

Ausnahmen und try/catch/finally das Paradigma von so ziemlich jede anderen großen Sprache (C++, Java, PHP verwendet wird, Ruby, Python). Vielleicht ist es an der Zeit, die Paranoia fallen zu lassen und sie auch zu umarmen ... zumindest in iOS.

+6

Es ist inkorrekt, dass die Verwendung von NSExceptions für behebbare Fehler und die Ablaufsteuerung in Ordnung ist. Die Frameworks unterstützen dies explizit nicht. – bbum

+0

Abgesehen von dem (wichtigeren) Punkt, den bbum macht, fällt es mir schwer zu sehen, wie try/catch die Ligibilität verbessert: Wenn alles, was du willst, nicht wiederhergestellt wird, kehre früh und oft zurück. Wenn Sie eine Kaskade von Fängen haben, wie ist das besser? – danyowdee

+2

Da Sie die Behandlung jedes Fehlertyps an einem einzelnen Ort für einen bestimmten Codeblock statt pro Zeile zentralisieren. –

Verwandte Themen