2009-06-26 2 views
8

Ich bin dabei, verschiedene Routinen zu programmieren, und ich versuche mein Bestes, um es ordentlich und refaktoriert zu halten.Korrekte Struktur zum Überprüfen auf Fehler mit NSError

Methoden Ich schaffe beginnen zu diesem Code ähnlich aussehen:

-(IBAction)buttonPress:(id)sender { 
    // Create Document Shopping List with this document 
    [self doSomething:&error]; 

    if(error) { 
     [NSApp presentError:&error]; 
     return nil; 
    } 

    [self doSomethingElse:&error]; 

    if(error) { 
     [NSApp presentError:&error]; 
     return nil; 
    } 

    [self doYetSomethingElse:&error]; 

    if(error) { 
     [NSApp presentError:&error]; 
     return nil; 
    } 
} 

Ich liebe NSError, aber dies scheint wie eine furchtbar ungeschickt Weise alle meine Fehler zu verarbeiten.

Ein paar Gedanken, die ich über alternative Möglichkeiten gehabt haben:

a) könnte die Fehlerprüfung in die Methoden doSomething, doSomethingElse usw. gebaut werden, aber dann würde ich nicht in der Lage sein, die Taste drücken Methode zu verlassen, ohne eine Art Überprüfung des Rückgabewertes, die mich zu einer ähnlichen Struktur zurückführen würde.

b) Ich könnte den NSError als Key Value Observed einrichten, aber etwas darüber fühlt sich zutiefst falsch an. Ich bin mir der Möglichkeiten von Missbrauch mit KVO sehr bewusst, also versuche ich alles ohne es zu machen, wo immer es möglich ist.

Sicher fehlt mir hier etwas wirklich Grundlegendes? Gibt es ein Muster, das mir helfen kann? Oder ist diese Struktur in Ordnung?

+0

Ich bin absolut erschüttert über die erstaunliche Antwort, die ich in den letzten 10 Minuten erhalten habe! Vielen Dank für Ihre Antworten. Sehr, sehr hilfreich. –

Antwort

11

Ich denke, dass keine der hier vorgestellten Antworten Apples empfohlene Praktiken berücksichtigt.

Grundsätzlich sollten Sie nie nach dem Objekt NSError suchen, um festzustellen, ob ein Problem aufgetreten ist. Sie überprüfen, ob ein Problem aufgetreten ist, indem Sie den Rückgabewert der Methode überprüfen, die einen Zeiger auf einen Zeiger auf NSError zurückgibt.

Von the Apple documentation:

Wichtige Erfolg oder Misserfolg wird durch den Rückgabewert der Methode angegeben. Obwohl Cocoa Methoden, die indirekt Fehlerobjekte in die Cocoa Fehler Domain zurückkehren garantiert solche Objekte zurückzugeben, wenn die Verfahren durch Fehler anzeigt nil oder NO direkt zurückkehren, sollten Sie immer prüfen, ob der Rückgabewert nil oder NO vor dem Versuch mach irgendetwas mit dem NSError Objekt.

+0

Einige Methoden wie sendAsynchronousRequest geben void zurück und das einzige, was Sie tun können, ist NSError zu überprüfen. Oder fehlt mir etwas? –

+2

In diesem Fall überprüfen Sie die anderen Parameter. Das heißt NSURLResponse und NSData. –

0

Hinweis: Als ich dies postete war mir keine MacOS/Cocoa Exceptions Debatte bekannt. Bitte beachten Sie dies, wenn Sie diese Antwort berücksichtigen.

Warum nicht stattdessen exceptions verwenden? Dadurch können Sie auf den Fehlerparameter Ihrer Methoden verzichten und Ihre Fehler an einer Stelle behandeln.

-(IBAction)buttonPress:(id)sender { 
    // Create Document Shopping List with this document 
    @try { 
     [self doSomething]; 
     [self doSomethingElse]; 
     [self doYetSomethingElse]; 
    } @catch (NSException* e) { 
     [NSApp presentException:e]; 
    } 
    return nil; 
} 

Sie brauchen natürlich Ausnahmen zu werfen, wie von erforderlichen innerhalb Ihrer doXXXX Methoden:

NSException* e = [NSException exceptionWithName:@"MyException" 
             reason:@"Something bad happened" 
             userInfo:nil]; 
@throw e; 
+3

Es gibt eine anhaltende Debatte darüber, ob die Verwendung von Ausnahmen für die Flusssteuerung in Mac OS X/Cocoa akzeptabel ist. Ich persönlich nehme die Position ein, die (die meisten von) Apple nimmt, dass Ausnahmen in Cocoa für außergewöhnliche Programmierfehler sind und daher nicht für die Ablaufsteuerung verwendet werden sollten. – danielpunkass

+0

Würde mich interessieren, wieso das abgesagt wurde. Was habe ich verpasst? – teabot

+2

Ah, danke, dass du es aufräumst @danielpunkass - Ich komme aus einem Java-Hintergrund, also habe ich Exceptions in Objective-C benutzt, ohne von einer solchen Debatte zu wissen. – teabot

1

Ich denke, die wirkliche Frage ist, wie viel Granularität Sie in Ihre Fehlerbehandlung benötigen. Ihr Code ist grundsätzlich korrekt - aber Sie können Dinge aufräumen, indem Sie weniger häufig nach Fehlern suchen oder einen Ansatz wie Teabot verwenden, der in seiner Antwort mit NSException erwähnt wird. Viele der integrierten Funktionen, die Fehler zurückgeben (wie zB moveItemAtPath :) von NSFileManager, geben zusätzlich zu einem NSError-Objekt ein BOOL zurück. Sie können also auch verschachtelte if-Anweisungen verwenden, wenn Sie die NSError-Informationen nicht benötigen.

Alles in allem, es gibt wirklich keine gute Möglichkeit, dies zu tun. Wenn Ihr Code lang ist, könnte KVO eine coole Lösung sein. Ich habe es nie für eine solche Situation versucht.

+0

Danke für den Kommentar, Ben. Idealerweise möchte ich alle Fehler einheitlich behandeln können - mit NSError. Ich bin etwas verwirrt von den Funktionen, die manchmal einen BOOL zurückgeben - wenn es einen Standardweg gibt, Fehler mit NSError zu behandeln, warum würde Apple die Gewässer durch die Bereitstellung von Rückgabewerten trüben? Das hat mich immer ein bisschen verwirrt. –

+1

Sich auf ein NSError-Objekt zu verlassen, ist, ehrlich gesagt, töricht. Es ist sehr einfach für jemanden, den einen Randfall zu übersehen, wo zu der Zeit der Code zum Erstellen eines Fehlerobjekts zu mühsam war, und die Methode daher fälschlicherweise Erfolg meldet. Aus diesem Grund behandelt Apple Fehlerinformationen so, wie sie es tun. Verwenden Sie einen einfachen Test einer Methode, die NO oder Nil zurückgibt, um festzustellen, dass sie fehlgeschlagen ist. DANN lassen Sie das NSError-Objekt (falls verfügbar) für weitere Details herunter. –

+0

Sehr guter Punkt, Mike. Gibt mir etwas anderes zu überlegen. Lies das nur, ich habe SO nicht schnell genug überprüft. –

2

Der Kern Ihrer Frage ist, ob es strukturelle Verbesserungen gibt, die Sie an Ihrer Fehlerbehandlung vornehmen können. Ich denke schon, indem ich im Wesentlichen mehr Ebenen der Verschachtelung einführe, entweder durch Extrahieren von mehr Code in separate Methoden/Funktionen oder durch Einfügen von Verschachtelung in Ihre High-Level-Beispielmethode.

Die Idee ist, wenn es darum geht, die meisten Fehler zu behandeln, sind Sie entweder daran interessiert, eine alternative Aufgabe durchzuführen, oder den Fehler in der Kette nach oben zu schicken, damit ein verantwortlicher Controller den Fehler durch den Benutzer übermitteln kann Benutzeroberfläche.

Mit dieser Idee der „propagieren oder zu handhaben“, würde ich Ihre Probe Methode wie folgt umschreiben:

-(IBAction)buttonPress:(id)sender { 

    // Create Document Shopping List with this document 
    [self doSomething:&error];  
    if(error == nil) { 
     [self doSomethingElse:&error]; 
     if (error == nil) { 
      [self doYetSomethingElse:&error]; 
     } 
    } 

    if(error) { 
     [NSApp presentError:&error]; 
    }  
} 

Hinweis, dass es gute Argumente gegen die in einem bestimmten Verfahren zu viel Verschachtelung einzuführen. Eine solche Verschachtelung ist im Wesentlichen eine kurze Alternative zum Extrahieren von Methoden. Es kann zum Beispiel sinnvoller sein, dass "doSomething:" selbst doSomethingElse: aufruft, was doYetSomethingElse aufruft: zum Beispiel. Dies würde dem Code die gleiche Struktur wie dem if-nest auferlegen, wäre aber wohl wartungsfreundlicher.

Nebenbei bin ich kein Fan von Inline-Return-Aussagen. In dieser bestimmten Instanz ruft die Beispielmethode keinen Rückgabewert auf, aber wenn dies der Fall ist, wird eine lokale Variable am Rückgabewert festgelegt und erst am Ende der Ablaufsteuerung zurückgegeben. Das vorzeitige Aufspringen einer Funktion oder Methode ist ein sicherer Weg, auf seltsame Bugs zu stoßen, IMHO.

+0

Danke für einen ausgezeichneten Vorschlag, Daniel. Ich habe mich davor gescheut, weil Code Complete empfiehlt, die Verschachtelung zu minimieren. Ich habe ein paar Alpträume verschachtelt, wenn .. andere Aussagen in meiner Zeit. Und wenn ich mehr als 5 Schritte hätte, würde das wohl mühsam werden. Auf der anderen Seite, wenn ich mehr als 5 Schritte hatte, würde ich denke, es wäre ein Warnsignal, dass mein Code sowieso refaktoriert werden musste. Und diese spezifische Verschachtelung ist immer noch gut lesbar und ist viel besser als mein Original, also werde ich das machen. Danke nochmal! –

+1

Froh, dass Sie die Antwort zu schätzen wissen. Ich denke auch an Code Complete und Minimierung der Verschachtelung, aber ich rationalisiere es wie oben beschrieben, um zu berücksichtigen, dass minimale Verschachtelung nur eine Kurzform für Code ist, der in eine andere Methode oder Funktion einbezogen werden kann, wenn die Umstände es erfordern. Im Allgemeinen denke ich, Verschachtelung ist besser als die Alternative, eine "run-on" -Funktion ... – danielpunkass

Verwandte Themen