Po co właściwie NSAssert?

155

Muszę o to zapytać, ponieważ: Jedyne, co rozpoznaję, to to, że jeśli asercja się nie powiedzie, aplikacja się zawiesza. Czy to jest powód, dla którego warto używać NSAssert? Albo jaka jeszcze jest z tego korzyść? I czy słuszne jest umieszczanie NSAssert tuż nad jakimkolwiek założeniem, które poczynię w kodzie, na przykład funkcji, która nigdy nie powinna otrzymywać -1 jako parametru, ale może mieć wartość -0,9 lub -1,1?

TheNeil
źródło

Odpowiedzi:

300

Assert to upewnienie się, że wartość jest taka, jaka powinna być. Jeśli asercja nie powiedzie się, oznacza to, że coś poszło nie tak i aplikacja zostaje zamknięta. Jednym z powodów użycia assert może być to, że masz jakąś funkcję, która nie będzie się zachowywać lub będzie powodować bardzo złe efekty uboczne, jeśli jeden z przekazanych do niej parametrów nie jest dokładnie jakąś wartością (lub zakresem wartości), którą możesz umieścić w asercie upewnij się, że wartość jest taka, jakiej się spodziewasz, a jeśli nie, to coś jest naprawdę nie tak i aplikacja kończy pracę. Assert może być bardzo przydatny do debugowania / testowania jednostkowego, a także wtedy, gdy dostarczasz frameworki, aby powstrzymać użytkowników przed robieniem „złych” rzeczy.

Daniel
źródło
9
Powinieneś wyjąć NSAssert do wydania. Do tego służy flaga kompilacji.
Barry Wark
127
> Powinieneś wyjąć NSAssert do wydania. To jest dyskusyjne. Zawsze udostępniam swoje aplikacje z włączonymi asercjami i jest to standardowa praktyka w przypadku wielu programów, na przykład robi to Apple. Gdy tylko Twój program wykryje nieprawidłowy stan, powinieneś zawiesić się. Możesz uzyskać ślad stosu, gdzie wystąpił błąd, natomiast jeśli wyłączysz potwierdzenia, możesz w końcu uszkodzić pamięć i / lub dane użytkownika, a problem będzie bardzo trudny do debugowania.
Mike Weller
18
Zauważ, że XCode 4 ma zdefiniowane NS_BLOCK_ASSERTIONS domyślnie w konfiguracjach wydania. Myślę, że jeśli nie zmienisz, twój wydany kod nie będzie zawierał NSAssert: s.
Jonny
16
Jeśli dobrze rozumiem, jaki jest sens ich pozostawiania (w wydaniu)? Dlaczego nie zamienić NSAssert na instrukcję if, a jeśli (dzieje się coś strasznego), to poinformuj użytkownika (lub zrób coś, co jest pod twoją kontrolą), a nie po prostu zamknij / wyłącz się i nie zostawiaj użytkownika zastanawiającego się, co się stało ... Lub brakuje mi czegoś
Gik
11
Szkoda czasu dewelopera, aby podążać ścieżką każdego wyjątkowego przypadku, który w normalnych okolicznościach w ogóle nie powinien mieć miejsca. Obejmuje to wymyślenie odpowiednich sposobów informowania użytkownika o każdym z nich i / lub uczynienie aplikacji na tyle solidną, aby działała w oczekiwany sposób po ich wystąpieniu. Bardziej praktycznym podejściem jest zawieszenie aplikacji i naprawienie błędu znalezionego w raporcie o awarii oraz wydanie nowej wersji. Mimo to ważne jest, aby upewnić się, że w takiej sytuacji nie nastąpi utrata danych. Trzeba to jednak zapewnić, ale jest to dużo mniejszy nakład pracy.
trss
20

Naprawdę nie mogę rozmawiać z NSAssert, ale wyobrażam sobie, że działa podobnie do assert () w C.

assert () służy do wymuszania kontraktu semantycznego w kodzie. Co to znaczy, pytasz?

Cóż, jest tak, jak powiedziałeś: jeśli masz funkcję, która nigdy nie powinna otrzymać -1, możesz wymusić assert ():

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

A teraz zobaczysz coś takiego w dzienniku błędów (lub STDERR):

Asercja i> 0 nie powiodła się: plik przykład.c, wiersz 2

Dlatego nie tylko chroni przed potencjalnie złymi danymi wejściowymi, ale rejestruje je w użyteczny, standardowy sposób.

Aha, i przynajmniej w C assert () było makrem, więc możesz przedefiniować assert () jako no-op w swoim kodzie wydania. Nie wiem, czy tak jest w przypadku NSAssert (a nawet assert ()), ale kompilacja tych sprawdzeń była całkiem przydatna.

Mando Escamilla
źródło
2
Tak, NSAssert również jest makrem.
Martin Wickman,
18

NSAssertdaje więcej niż tylko awarię aplikacji. Informuje o klasie, metodzie i wierszu, w którym wystąpiło potwierdzenie. Wszystkie asercje można również łatwo dezaktywować za pomocą NS_BLOCK_ASSERTIONS. Dzięki temu jest bardziej odpowiedni do debugowania. Z drugiej strony, rzucenie NSExceptionjedynego powoduje awarię aplikacji. Nie informuje również o lokalizacji wyjątku ani nie można go tak łatwo wyłączyć. Zobacz różnicę na poniższych obrazkach.

Aplikacja ulega awarii, ponieważ asercja wywołuje również wyjątek, zgodnie z dokumentacją NSAssert :

Wywołany program obsługi asercji drukuje komunikat o błędzie zawierający nazwę metody i klasy (lub nazwę funkcji). Następnie zgłasza wyjątek NSInternalInconsistencyException.

NSAssert:

Dzienniki po asercji

NSException:

Dzienniki po wyjątku

Abdurrahman Mubeen Ali
źródło
NSExceptionZapewnia mnóstwo okazji, aby dostosować wyjście powraca za pośrednictwem reasoni userInfoparametrów. Nie ma powodu, dla którego nie mógłbyś dodać nazwy klasy, selektora, informacji o wierszu ani niczego innego, co chcesz dodać, aby pomóc w debugowaniu. IMHO, używasz NSAssertdo celów debugowania podczas programowania, ale wyłączasz je do wysyłki; rzucasz, NSExceptionjeśli chcesz zostawić w asercji w kodzie wysyłkowym.
markeissler
17

Oprócz tego, co wszyscy powiedzieli powyżej, domyślnym zachowaniem NSAssert()(w przeciwieństwie do C assert()) jest wyrzucenie wyjątku, który można złapać i obsłużyć. Na przykład Xcode to robi.

Jens Ayton
źródło
Czy jest więcej informacji na temat tego, jak możemy złapać i obsłużyć wyjątek?
Gon
1
Wyjątki w kakao de facto NIE są „możliwe do złapania i przetworzenia”. Jeśli sterowanie w ogóle przechodzi przez funkcję jabłka w dowolnym miejscu drzewa wywołań, zachowanie jest niezdefiniowane. Wyjątki służą wyłącznie do zgłaszania błędów (aka, krytyka itp.), A nie do ogólnego użytku, jak w Javie.
Michael,
9

Aby wyjaśnić, jak ktoś wspomniał, ale nie w pełni wyjaśniono, powodem posiadania i używania potwierdzeń zamiast tworzenia niestandardowego kodu (na przykład robienie ifs i zgłaszanie wyjątku dla złych danych) jest to, że twierdzi, że POWINNO być wyłączone dla aplikacji produkcyjnych.

Podczas programowania i debugowania potwierdzenia są włączone, abyś mógł wychwycić błędy. Program zatrzyma się, gdy potwierdzenie zostanie ocenione jako fałszywe. Ale podczas kompilacji na potrzeby produkcyjne kompilator pomija kod potwierdzenia i w rzeczywistości sprawia, że ​​program działa szybciej. Miejmy nadzieję, że do tego czasu naprawiłeś wszystkie błędy. W przypadku, gdy twój program nadal zawiera błędy podczas produkcji (kiedy asercje są wyłączone i program „pomija” asercje), twój program prawdopodobnie zakończy się awarią w innym miejscu.

Z pomocy NSAssert: "Asercje są wyłączone, jeśli zdefiniowano makro preprocesora NS_BLOCK_ASSERTIONS." Po prostu umieść makro w docelowym miejscu dystrybucji [tylko].

Ohad Kravchick
źródło
6

NSAssert(i jego odpowiednik w standardowej bibliotece assert) służą do wykrywania błędów programowania podczas programowania. Nigdy nie powinieneś mieć asercji, która zawodzi w produkcyjnej (wydanej) aplikacji. Możesz więc twierdzić, że nigdy nie przekazujesz liczby ujemnej do metody wymagającej argumentu dodatniego. Jeśli asercja kiedykolwiek zawiedzie podczas testowania, masz błąd. Jeśli jednak wartość, która jest przekazywana, jest wprowadzana przez użytkownika, należy przeprowadzić odpowiednią walidację danych wejściowych, zamiast polegać na potwierdzeniu w środowisku produkcyjnym (można ustawić #define dla kompilacji wydania, które wyłączają NSAssert*.

Barry Wark
źródło
2
+1, ponieważ Twoja odpowiedź jest dla mnie najbardziej sensowna! Używanie NSAssert jest bardziej sensowne, jeśli jest używane do celów programistycznych, a nie po wydaniu. Po wpisaniu przez użytkownika wartości, która jest niedozwolona, ​​powinien nastąpić błąd interfejsu użytkownika, a nie NSAssert powodujący awarię aplikacji. Mgła ustąpiła!
pnizzle
3

Twierdzenia są powszechnie używane do wymuszania zamierzonego użycia określonej metody lub elementu logiki. Powiedzmy, że piszesz metodę, która oblicza sumę dwóch liczb całkowitych większych od zera. Aby upewnić się, że metoda była zawsze używana zgodnie z przeznaczeniem, prawdopodobnie umieściłbyś asercję, która testuje ten warunek.

Krótka odpowiedź: wymuszają, że twój kod jest używany tylko zgodnie z przeznaczeniem.

Salony
źródło
3

Warto zauważyć, że oprócz sprawdzania w czasie wykonywania, programowanie potwierdzające jest ważną funkcją używaną podczas projektowania kodu na podstawie umowy.

Więcej informacji na temat stwierdzenia i projektowania na podstawie umowy można znaleźć poniżej:

Assertion (rozwój oprogramowania)

Zaprojektuj zgodnie z umową

Programowanie z asercjami

Zaprojektuj według umowy, na przykładzie [książka w miękkiej okładce]

Dar
źródło
2

Aby w pełni odpowiedzieć na jego pytanie, celem każdego typu potwierdzenia jest pomoc w debugowaniu. Bardziej wartościowe jest wychwycenie błędów u ich źródła, a następnie wyłapanie ich w debugerze, gdy powodują one awarie.

Na przykład, możesz przekazać wartość do funkcji, która oczekuje wartości z określonego zakresu. Funkcja może przechowywać wartość do późniejszego użycia, a przy późniejszym użyciu aplikacja ulega awarii. Stos wywołań widoczny w tym scenariuszu nie pokaże źródła złej wartości. Lepiej jest złapać złą wartość, gdy pojawia się, aby dowiedzieć się, kto przekazuje złą wartość i dlaczego.

Robert Hawkey
źródło
-3

NSAssertpowoduje awarię aplikacji, gdy pasuje do warunku. Jeśli nie pasuje do warunku, zostaną wykonane następne instrukcje. Poszukaj EX poniżej:

Po prostu tworzę aplikację, aby przetestować, jakie jest zadanie NSAssert:

    - (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");
}

w moim kodzie aplikacja nie ulegnie awarii, a przypadek testowy to:

  • anNum > = 2 -> Aplikacja nie ulegnie awarii i możesz zobaczyć ciąg dziennika: „Ta instrukcja zostanie wykonana, gdy anNum <2” w oknie konsoli dziennika wyjścia
  • anNum <2 -> Aplikacja ulegnie awarii i nie widać ciągu dziennika: „Ta instrukcja zostanie wykonana, gdy anNum <2”

źródło
1
Masz to na odwrót. „NSAssert powoduje awarię aplikacji, gdy spełnia ona warunek. Jeśli nie spełnia warunku, zostaną wykonane następne instrukcje”. NSAssert zawiesza aplikację, jeśli NIE spełnia ona warunku, i uruchamia się normalnie, jeśli spełnia warunek.
Joe Tam
Aplikacja ulega awarii i rejestruje komunikat, gdy nie spełnia warunku, w przeciwnym razie jest wykonywana dalej.
Pravin S.