Was ist eigentlich der Sinn von NSAssert?

155

Ich muss das fragen, weil: Das einzige, was ich erkenne, ist, dass die App abstürzt, wenn die Behauptung fehlschlägt. Ist das der Grund, warum NSAssert verwendet wird? Oder was bringt es sonst noch? Und ist es richtig, einen NSAssert direkt über jede Annahme zu setzen, die ich im Code mache, wie eine Funktion, die niemals einen -1 als Parameter erhalten sollte, sondern einen -0,9 oder -1,1?

TheNeil
quelle

Antworten:

300

Assert soll sicherstellen, dass ein Wert so ist, wie er sein soll. Wenn eine Behauptung fehlschlägt, bedeutet dies, dass ein Fehler aufgetreten ist und die App beendet wird. Ein Grund für die Verwendung von assert wäre, wenn Sie eine Funktion haben, die sich nicht verhält oder sehr schlimme Nebenwirkungen hervorruft, wenn einer der übergebenen Parameter nicht genau ein Wert (oder ein Wertebereich) ist, den Sie für eine Assert festlegen können Stellen Sie sicher, dass dieser Wert Ihren Erwartungen entspricht. Wenn dies nicht der Fall ist, stimmt etwas nicht, und die App wird beendet. Assert kann sehr nützlich sein für Debugging / Unit-Tests und auch, wenn Sie Frameworks bereitstellen, um die Benutzer davon abzuhalten, "böse" Dinge zu tun.

Daniel
quelle
9
Sie sollten NSAssert zur Veröffentlichung herausnehmen. Dafür gibt es ein Flag zur Kompilierungszeit.
Barry Wark
127
> Sie sollten NSAssert zur Freigabe herausnehmen. Das ist umstritten. Ich gebe meine Anwendungen immer mit aktivierten Zusicherungen frei, und dies ist Standard für viele Softwareprogramme, zum Beispiel für Apple. Sobald Ihr Programm einen abnormalen Zustand festgestellt hat, sollten Sie abstürzen. Sie können einen Stack-Trace abrufen, in dem angegeben ist, wo der Fehler aufgetreten ist. Wenn Sie jedoch Asserts deaktivieren, können Speicher- und / oder Benutzerdaten beschädigt werden, und das Problem ist nur schwer zu beheben.
Mike Weller
18
Beachten Sie, dass in XCode 4 in Release-Konfigurationen standardmäßig NS_BLOCK_ASSERTIONS definiert ist. Ich denke, wenn Sie nicht ändern, dass Ihr veröffentlichter Code keine NSAssert: s enthält.
Jonny
16
Wenn ich das richtig verstehe, was bringt es, sie zu verlassen (in der Release-Version)? Warum nicht den NSAssert durch eine if-Anweisung ersetzen und if (etwas Schreckliches passiert), dann den Benutzer informieren (oder etwas tun, das unter Ihrer Kontrolle steht) und nicht einfach beenden / abstürzen und den Benutzer fragen lassen, was passiert ist ... Oder vermisse ich etwas
Gik
11
Es ist eine Verschwendung von Entwicklerzeit, den Weg eines jeden Ausnahmefalls zu beschreiten, der unter normalen Umständen überhaupt nicht passieren sollte. Dies beinhaltet die Überlegung geeigneter Möglichkeiten, den Benutzer für jeden von ihnen zu informieren und / oder die App robust genug zu machen, um nach ihrem Auftreten in erwarteter Weise fortzufahren. Der praktischere Ansatz besteht darin, die App zum Absturz zu bringen, den im Absturzbericht gefundenen Fehler zu beheben und eine neue Version zu veröffentlichen. Trotzdem ist es wichtig sicherzustellen, dass in einer solchen Situation kein Datenverlust auftritt. Dies muss zwar sichergestellt werden, ist aber weitaus weniger Arbeit.
trss
20

Ich kann nicht wirklich mit NSAssert sprechen, aber ich stelle mir vor, dass es ähnlich wie Cs assert () funktioniert.

assert () wird verwendet, um einen semantischen Vertrag in Ihrem Code durchzusetzen. Was heißt das, fragst du?

Nun, es ist wie Sie gesagt haben: Wenn Sie eine Funktion haben, die niemals eine -1 erhalten sollte, können Sie assert () dies erzwingen lassen:

void gimme_positive_ints (int i) {
  behaupten (i> 0);
}}

Und jetzt sehen Sie so etwas im Fehlerprotokoll (oder STDERR):

Behauptung i> 0 fehlgeschlagen: Datei example.c, Zeile 2

Es schützt also nicht nur vor potenziell schlechten Eingaben, sondern protokolliert sie auch auf nützliche und standardmäßige Weise.

Oh, und zumindest in C war assert () ein Makro, sodass Sie assert () in Ihrem Release-Code als No-Op neu definieren konnten. Ich weiß nicht, ob dies bei NSAssert der Fall ist (oder sogar mehr behauptet ()), aber es war ziemlich nützlich, diese Prüfungen zu kompilieren.

Mando Escamilla
quelle
2
Ja, NSAssert ist auch ein Makro.
Martin Wickman
18

NSAssertbietet Ihnen mehr als nur einen Absturz der App. Es zeigt Ihnen die Klasse, Methode und die Zeile, in der die Zusicherung erfolgte. Alle Zusicherungen können auch einfach mit NS_BLOCK_ASSERTIONS deaktiviert werden. Dadurch eignet es sich besser zum Debuggen. Auf der anderen Seite NSExceptionstürzt das Werfen eines nur die App ab. Es gibt auch keinen Hinweis auf den Ort der Ausnahme und kann auch nicht so einfach deaktiviert werden. Sehen Sie den Unterschied in den Bildern unten.

Die App stürzt ab, weil eine Zusicherung auch eine Ausnahme auslöst , wie in der NSAssert-Dokumentation angegeben :

Beim Aufrufen gibt ein Assertion-Handler eine Fehlermeldung aus, die die Methoden- und Klassennamen (oder den Funktionsnamen) enthält. Anschließend wird eine NSInternalInconsistencyException-Ausnahme ausgelöst.

NSAssert:

Protokolliert nach einer Zusicherung

NSException:

Protokolliert nach einer Ausnahme

Abdurrahman Mubeen Ali
quelle
A NSExceptionbietet zahlreiche Möglichkeiten, die über die Parameter reasonund zurückgegebene Ausgabe anzupassen userInfo. Es gibt keinen Grund, warum Sie Klassennamen, Selektor, Zeileninformationen und alles andere, was Sie hinzufügen möchten, nicht hinzufügen können, um das Debuggen zu unterstützen. IMHO, Sie verwenden ein NSAssertfür Debugging-Zwecke während der Entwicklung, aber deaktivieren sie für den Versand; Sie werfen eine, NSExceptionwenn Sie in einer Aussage im Versandcode verlassen möchten.
markeissler
17

Abgesehen von dem, was alle oben gesagt haben, besteht das Standardverhalten von NSAssert()(im Gegensatz zu Cs assert()) darin, eine Ausnahme auszulösen, die Sie abfangen und behandeln können. Zum Beispiel macht Xcode dies.

Jens Ayton
quelle
Gibt es mehr darüber, wie wir die Ausnahme abfangen und behandeln können?
Gon
1
Ausnahmen bei Kakao sind de facto NICHT "fangbar und handhabbar". Wenn die Steuerung an einer beliebigen Stelle im Aufrufbaum eine Apple-Funktion durchläuft, ist das Verhalten undefiniert. Ausnahmen gelten ausschließlich für die Fehlerberichterstattung (auch bekannt als Crittercism usw.) und nicht für den allgemeinen Gebrauch wie in Java.
Michael
9

Um zu verdeutlichen, wie bereits erwähnt, aber nicht vollständig erklärt, ist der Grund für das Vorhandensein und Verwenden von Asserts, anstatt nur benutzerdefinierten Code zu erstellen (z. B. ifs und Auslösen einer Ausnahme für fehlerhafte Daten), dass Asserts für Produktionsanwendungen deaktiviert werden sollten.

Während der Entwicklung und des Debuggens werden Asserts aktiviert, damit Sie Fehler abfangen können. Das Programm wird angehalten, wenn eine Zusicherung als falsch bewertet wird. Beim Kompilieren für die Produktion lässt der Compiler jedoch den Assertionscode weg und lässt Ihr Programm schneller laufen. Bis dahin haben Sie hoffentlich alle Fehler behoben. Falls Ihr Programm während der Produktion immer noch Fehler aufweist (wenn Zusicherungen deaktiviert sind und das Programm die Zusicherungen "überspringt"), stürzt Ihr Programm wahrscheinlich an einem anderen Punkt ab.

Aus der Hilfe von NSAssert: "Zusicherungen sind deaktiviert, wenn das Präprozessor-Makro NS_BLOCK_ASSERTIONS definiert ist." Fügen Sie das Makro einfach [nur] in Ihr Verteilungsziel ein.

Ohad Kravchick
quelle
6

NSAssert(und sein stdlib-Äquivalent assert) dienen dazu, Programmierfehler während der Entwicklung zu erkennen. Sie sollten niemals eine Behauptung haben, die in einer (freigegebenen) Produktionsanwendung fehlschlägt. Sie könnten also behaupten, dass Sie niemals eine negative Zahl an eine Methode übergeben, die ein positives Argument erfordert. Wenn die Behauptung während des Testens jemals fehlschlägt, liegt ein Fehler vor. Wenn der übergebene Wert jedoch vom Benutzer eingegeben wird, müssen Sie die Eingabe ordnungsgemäß validieren, anstatt sich auf die Zusicherung in der Produktion zu verlassen (Sie können ein #define für Release-Builds festlegen, die deaktiviert werden NSAssert*.

Barry Wark
quelle
2
+1, weil deine Antwort für mich am sinnvollsten ist! Die Verwendung von NSAssert ist sinnvoller, wenn es für die Entwicklung verwendet wird, nicht nach der Veröffentlichung. Einem Benutzer, der einen nicht zulässigen Wert eingibt, sollte ein UI-Fehler folgen, nicht NSAssert, der die Anwendung zum Absturz bringt. Der Nebel hat sich verzogen!
pnizzle
3

Behauptungen werden üblicherweise verwendet, um die beabsichtigte Verwendung einer bestimmten Methode oder Logik zu erzwingen. Angenommen, Sie haben eine Methode geschrieben, die die Summe von zwei Ganzzahlen größer als Null berechnet. Um sicherzustellen, dass die Methode immer wie beabsichtigt verwendet wurde, würden Sie wahrscheinlich eine Zusicherung machen, die diese Bedingung testet.

Kurze Antwort: Sie erzwingen, dass Ihr Code nur bestimmungsgemäß verwendet wird.

Lounges
quelle
2

Um seine Frage vollständig zu beantworten, besteht der Sinn jeder Art von Behauptung darin, das Debuggen zu unterstützen. Es ist wertvoller, Fehler an ihrer Quelle abzufangen und sie dann im Debugger abzufangen, wenn sie Abstürze verursachen.

Sie können beispielsweise einen Wert an eine Funktion übergeben, die Werte in einem bestimmten Bereich erwartet. Die Funktion kann den Wert für die spätere Verwendung speichern und bei späterer Verwendung stürzt die Anwendung ab. Der in diesem Szenario angezeigte Aufrufstapel zeigt nicht die Quelle des fehlerhaften Werts an. Es ist besser, den schlechten Wert zu erfassen, um herauszufinden, wer den schlechten Wert übergibt und warum.

Robert Hawkey
quelle
-3

NSAssertApp zum Absturz bringen, wenn sie mit der Bedingung übereinstimmt. Wenn dies nicht mit der Bedingung übereinstimmt, werden die nächsten Anweisungen ausgeführt. Suchen Sie nach der EX unten:

Ich erstelle einfach eine App, um zu testen, was die Aufgabe NSAssertist:

    - (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self testingFunction:2];
}

-(void)testingFunction: (int)anNum{
    // if anNum < 2 -> the app will crash
    // and the NSLog statement will not execute
    // that mean you cannot see the string: "This statement will execute when anNum < 2"
    // into the log console window of Xcode
    NSAssert(anNum >= 2, @"number you enter less than 2");
    // If anNum >= 2 -> the app will not crash and the below 
    // statement will execute
    NSLog(@"This statement will execute when anNum < 2");
}

In meinem Code stürzt die App nicht ab. Und der Testfall ist:

  • anNum > = 2 -> Die App stürzt nicht ab und Sie können die Protokollzeichenfolge sehen: "Diese Anweisung wird ausgeführt, wenn anNum <2" im Fenster der outPut-Protokollkonsole angezeigt wird
  • anNum <2 -> Die App stürzt ab und Sie können die Protokollzeichenfolge nicht sehen: "Diese Anweisung wird ausgeführt, wenn anNum <2"

quelle
1
Du hast es umgekehrt. "NSAssert lässt die App abstürzen, wenn sie mit der Bedingung übereinstimmt. Wenn sie nicht mit der Bedingung übereinstimmt, werden die nächsten Anweisungen ausgeführt." NSAssert stürzt die App ab, wenn sie NICHT der Bedingung entspricht, und wird normal ausgeführt, wenn sie der Bedingung entspricht.
Joe Tam
Die App stürzt ab und protokolliert eine Nachricht, wenn sie die Bedingung nicht erfüllt, andernfalls wird sie weiter ausgeführt.
Pravin S.