zgłaszanie wyjątku w celu c / kakao

Odpowiedzi:

528

Używam [NSException raise:format:]w następujący sposób:

[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
e.James
źródło
9
Wolę ten sposób w stosunku do @throw([NSException exceptionWith…])podejścia, ponieważ jest bardziej zwięzły.
Sam Soffes,
9
Przeczytaj ważne ostrzeżenie przed
szkodami
26
Generalnie też wolę to, ale jest jedna gotcha. Może to po prostu moja bieżąca wersja Xcode, ale składnia [NSException raise ...] nie wydaje się być rozpoznawana przez parser jako ścieżka wyjścia z metody zwracającej wartość. Podczas używania tej składni widzę ostrzeżenie „Kontrola może osiągnąć koniec funkcji nieważnej”, ale przy składni @throw ([wyjątek NSExceptionWith…]) analizator rozpoznaje to jako wyjście i nie wyświetla ostrzeżenia.
mattorb
1
@mpstx Zawsze używam składni throw z podanego przez ciebie powodu (który jest nadal istotny dwa lata później w Xcode 4.6 i prawdopodobnie zawsze będzie). Posiadanie przez IDE rozpoznania, że ​​zgłoszenie wyjątku jest punktem wyjścia funkcji, często ma znaczenie, jeśli chcesz uniknąć ostrzeżeń.
Mark Amery
FWIW Zauważam, że bloki @ try / @ catch również powodują fałszywie ujemny dla ostrzeżeń „kontrola osiąga koniec funkcji nie-void” (tzn. Ostrzeżenie nie jest wyświetlane, kiedy powinno być)
Brian Gerstle
256

Słowo ostrzeżenia tutaj. W Objective-C, w przeciwieństwie do wielu podobnych języków, na ogół powinieneś unikać stosowania wyjątków dla typowych sytuacji błędów, które mogą wystąpić podczas normalnej pracy.

Dokumentacja Apple dla Obj-C 2.0 stwierdza, co następuje: „Ważne: Wyjątki wymagają dużego zasobu w celu C. Nie należy używać wyjątków do ogólnej kontroli przepływu lub po prostu do oznaczania błędów (takich jak brak dostępu do pliku)”

Dokumentacja Apple dotycząca obsługi wyjątków pojęciowych wyjaśnia to samo, ale z większą ilością słów: „Ważne: powinieneś zarezerwować wyjątki w programowaniu lub nieoczekiwane błędy w czasie wykonywania, takie jak dostęp do kolekcji poza zakresem, próby mutacji niezmiennych obiektów, wysyłanie nieprawidłowej wiadomości i utrata połączenia z serwerem okien. Zazwyczaj zajmujesz się tego rodzaju błędami z wyjątkami, gdy tworzona jest aplikacja, a nie w środowisku wykonawczym. [.....] Zamiast wyjątków, obiekty błędów (NSError) i Mechanizm dostarczania błędów kakao to zalecany sposób komunikowania oczekiwanych błędów w aplikacjach kakaowych. ”

Powodem tego jest częściowo przestrzeganie idiomów programistycznych w Celu C (używanie zwracanych wartości w prostych przypadkach i parametry odniesienia (często klasa NSError) w bardziej złożonych przypadkach), częściowo dlatego, że zgłaszanie wyjątków jest znacznie droższe i wreszcie (i co najważniejsze), że wyjątki Objective-C są cienkim opakowaniem wokół funkcji setjmp () i longjmp () C, co zasadniczo psuje twoją ostrożną obsługę pamięci, zobacz to wyjaśnienie .

szkody
źródło
11
Myślę, że dotyczy to większości języków programowania: „staraj się unikać wyjątków w przypadku typowych błędów”. To samo dotyczy Java; niewłaściwą praktyką jest obsługa błędów wprowadzania danych przez użytkownika (na przykład) z wyjątkami. Nie tylko ze względu na wykorzystanie zasobów, ale także dla przejrzystości kodu.
beetstra
6
Co ważniejsze, wyjątki w kakao mają na celu wskazanie nieodwracalnych błędów programu. Postępowanie w inny sposób jest niezgodne z ramami i może prowadzić do nieokreślonego zachowania. Szczegółowe informacje można znaleźć na stronie stackoverflow.com/questions/3378696/iphone-try-end-try/ ...
KPM
9
„To samo dotyczy Java;” Nie zgadzać się. Sprawdzonych wyjątków w Javie można używać w porządku dla normalnych warunków błędu. Oczywiście nie użyłbyś wyjątków Runtime.
Daniel Ryan
Wolę drogę kakao (wyjątkami są jedynie błędów programista) więc ja wolę to zrobić w Javie, a także, ale rzeczywistość jest taka, że należy udać się z typowych praktyk w środowisku, a wyjątki dla obsługi błędów kije się jak nieprzyjemny zapach w Objective-C, ale są często używane do tego celu w Javie.
gnasher729
1
Ten komentarz nie odpowiada na pytanie. Być może PO chce tylko zawiesić aplikację, aby sprawdzić, czy struktura raportu o awarii działa zgodnie z oczekiwaniami.
Simon
62
@throw([NSException exceptionWith…])

Xcode rozpoznaje @throwinstrukcje jako punkty wyjścia funkcji, takie jak returninstrukcje. Użycie @throwskładni pozwala uniknąć błędnych ostrzeżeń „ Kontrola może osiągnąć koniec funkcji nieważnych ”, które możesz uzyskać [NSException raise:…].

Ponadto @throwmożna go używać do rzucania obiektów, które nie należą do klasy NSException.

Peter Hosey
źródło
11
@Steph Thirion: Zobacz wszystkie developer.apple.com/documentation/Cocoa/Conceptual/Exceptions/ ... Dolna linia? Oba będą działać, ale @throw może służyć do rzucania obiektami, które nie należą do klasy NSException.
e.James
33

Jeżeli chodzi [NSException raise:format:]. W przypadku osób wywodzących się ze środowiska Java przypomnisz sobie, że Java rozróżnia wyjątki od wyjątków RuntimeException. Wyjątek jest sprawdzonym wyjątkiem, a RuntimeException nie jest zaznaczone. W szczególności Java sugeruje stosowanie sprawdzonych wyjątków dla „normalnych warunków błędów” i niezaznaczonych wyjątków dla „błędów czasu wykonania spowodowanych błędem programisty”. Wygląda na to, że wyjątków Objective-C należy używać w tych samych miejscach, w których użyjesz niesprawdzonego wyjątku, a wartości zwracane przez kod błędu lub wartości NSError są preferowane w miejscach, w których użyjesz sprawdzonego wyjątku.

Daniel Jankowski
źródło
1
Tak, jest to poprawne (jednak po 4 latach: D), stwórz własną klasę błędów ABCError, która rozciąga się na klasę NSError i używaj jej dla sprawdzonych wyjątków, a nie NSException. Podnieś wyjątki NSE, w których występują błędy programisty (nieoczekiwana sytuacja, taka jak problem z formatem liczbowym).
chathuram
15

Myślę, że aby zachować spójność, korzystniej jest używać @throw z własną klasą, która rozszerza NSException. Następnie używasz tych samych oznaczeń, aby w końcu spróbować złapać:

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

Apple wyjaśnia tutaj, jak zgłaszać i obsługiwać wyjątki: Łapanie wyjątków Zgłaszanie wyjątków

rustyshelf
źródło
Nadal miałem awarię z powodu wyjątku czasu wykonywania w Try Block
famfamfam
14

Od ObjC 2.0 wyjątki Objective-C nie są już opakowaniem dla setjmp () longjmp () C i są kompatybilne z wyjątkiem C ++, @try jest „bezpłatny”, ale rzucanie i łapanie wyjątków jest znacznie droższe.

W każdym razie, asercje (wykorzystujące rodzinę makr NSAssert i NSCAssert) rzucają NSException, i to rozsądne, aby używać ich jako stanów Ries.

Psycho
źródło
Dobrze wiedzieć! Mamy bibliotekę strony trzeciej, której nie chcemy modyfikować, która generuje wyjątki nawet w przypadku najmniejszych błędów. Musimy złapać je w jednym miejscu w aplikacji i to tylko powoduje, że się kulimy, ale to sprawia, że ​​czuję się trochę lepiej.
Yuri Brigance
8

Użyj NSError do komunikowania awarii zamiast wyjątków.

Szybkie punkty na temat NSError:

  • NSError pozwala kodom błędów w stylu C (liczbom całkowitym) jasno identyfikować główną przyczynę i, mam nadzieję, pozwolić programowi obsługi błędów na usunięcie błędu. Możesz bardzo łatwo zawijać kody błędów z bibliotek C, takich jak SQLite, w instancjach NSError.

  • NSError ma również tę zaletę, że jest obiektem i oferuje sposób bardziej szczegółowego opisania błędu za pomocą elementu słownika userInfo.

  • Ale co najlepsze, NSError NIE MOŻE zostać wyrzucony, dlatego zachęca do bardziej proaktywnego podejścia do obsługi błędów, w przeciwieństwie do innych języków, które po prostu rzucają gorącym ziemniakiem coraz dalej i dalej w górę stosu wywołań, w którym to momencie można go zgłosić tylko użytkownikowi i nie są traktowane w żaden znaczący sposób (nie, jeśli wierzysz w przestrzeganie największej zasady ukrywania informacji przez OOP).

Link referencyjny : referencja

Jason Fuerstenberg
źródło
Ten komentarz nie odpowiada na pytanie. Być może PO chce tylko zawiesić aplikację, aby sprawdzić, czy struktura raportu o awarii działa zgodnie z oczekiwaniami.
Simon
7

Oto jak nauczyłem się tego z „Przewodnika po wielkim nerdach (wydanie 4)”:

@throw [NSException exceptionWithName:@"Something is not right exception"
                               reason:@"Can't perform this operation because of this or that"
                             userInfo:nil];
Johannes
źródło
OK, ale niewiele mówi o userInfo:nil. :)
Cur
6

Możesz użyć dwóch metod zgłaszania wyjątku w bloku try catch

@throw[NSException exceptionWithName];

lub druga metoda

NSException e;
[e raise];
Subbu
źródło
3

Uważam, że nigdy nie należy używać wyjątków do kontrolowania normalnego przebiegu programu. Ale należy zgłaszać wyjątki, gdy jakaś wartość nie pasuje do pożądanej wartości.

Na przykład, jeśli jakaś funkcja akceptuje wartość, a ta wartość nigdy nie może być zerowa, wtedy można wyśledzić wyjątek, zamiast próbować zrobić coś „inteligentnego” ...

Ries

R. van Twisk
źródło
0

Wyjątki powinieneś zgłaszać tylko wtedy, gdy znajdziesz się w sytuacji, która wskazuje na błąd programowania i chcesz zatrzymać działanie aplikacji. Dlatego najlepszym sposobem zgłaszania wyjątków jest użycie makr NSAssert i NSParameterAssert i upewnienie się, że NS_BLOCK_ASSERTIONS nie jest zdefiniowany.

gnasher729
źródło
0

Przykładowy kod przypadku: @throw ([wyjątek NSExceptionWithName: ...

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

}

Za pomocą:

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

Kolejny bardziej zaawansowany przypadek użycia:

- (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.
źródło
-7

Nie ma powodu, aby nie używać wyjątków zwykle w celu C, nawet w celu oznaczenia wyjątków od reguł biznesowych. Apple może powiedzieć NSError, kogo to obchodzi. Obj C istnieje już od dawna i kiedyś dokumentacja ALL C ++ powiedziała to samo. Powodem, dla którego nie ma znaczenia, jak drogie jest rzucanie i łapanie wyjątku, jest fakt, że czas jego trwania jest wyjątkowo krótki i ... jest to WYJĄTEK od normalnego przepływu. Nigdy w życiu nie słyszałam, żeby ktoś powiedział, że wyjątek ten wymagał dużo czasu, by zostać rzuconym i złapanym.

Są też ludzie, którzy uważają, że sam cel C jest zbyt drogi i zamiast tego koduje w C lub C ++. Mówienie, że zawsze używaj NSError, jest niedoinformowane i paranoiczne.

Ale pytanie o ten wątek nie zostało jeszcze wyjaśnione, jaki jest NAJLEPSZY sposób na zgłoszenie wyjątku. Sposoby zwrotu NSError są oczywiste.

Czy to jest: [NSException podwyżka: ... @throw [[Alokacja NSException] initWithName .... lub @ throw [[MyCustomException ...?

Używam tutaj zaznaczonej / niezaznaczonej reguły nieco inaczej niż powyżej.

Istotna różnica między (przy użyciu metafory Java) zaznaczonym / niezaznaczonym jest ważna -> czy można odzyskać od wyjątku. Przez odzyskiwanie rozumiem nie tylko nie awarię.

Używam więc niestandardowych klas wyjątków z @throw do wyjątków możliwych do odzyskania, ponieważ jest prawdopodobne, że będę miał metodę aplikacji, która szuka pewnych rodzajów awarii w wielu blokach @catch. Na przykład, jeśli moja aplikacja jest bankomatem, miałbym blok @catch dla „WithdrawalRequestExceedsBalanceException”.

Używam NSException: podwyżka dla wyjątków środowiska wykonawczego, ponieważ nie mam sposobu na odzyskanie od wyjątku, oprócz złapania go na wyższym poziomie i zarejestrowania go. I nie ma sensu tworzyć do tego niestandardowej klasy.

W każdym razie to właśnie robię, ale jeśli istnieje lepszy, równie wyrazisty sposób, chciałbym również wiedzieć. W moim własnym kodzie, odkąd już dawno przestałem kodować C hella, nigdy nie zwracam NSError, nawet jeśli przechodzę przez API.

usunięty użytkownik
źródło
4
Poleciłbym spróbować zaprogramować serwer z wyjątkami jako część normalnego przepływu przypadków błędów przed sformułowaniem takich uogólniających stwierdzeń, jak: „nie ma powodu, aby nie używać wyjątków normalnie w celu C”. Wierzcie lub nie, istnieją powody, aby pisać aplikacje ObjC o wysokiej wydajności (lub przynajmniej ich części), a zgłaszanie wyjątków zwykle poważnie ogranicza wydajność.
jbenet
6
Są naprawdę bardzo dobre powody, dla których nie należy używać wyjątków w kakao. Zobacz odpowiedź Billa Bumgarnera tutaj: stackoverflow.com/questions/3378696/iphone-try-end-try/… . Wie, o czym mówi (wskazówka: sprawdź swojego pracodawcę). Wyjątki w kakao są traktowane jako błędy niemożliwe do odzyskania i mogą pozostawić system w niestabilnym stanie. NSError to sposób na przekazywanie ogólnych błędów.
Brad Larson
Wyjątki są wyjątkowe . Awarie reguł biznesowych z pewnością się nie kwalifikują. „Znalezienie i zaprojektowanie ciężkiego kodu wyjątkowego może doprowadzić do przyzwoitej wygranej”. MSDN via codinghorror.com/blog/2004/10/…
Jonathan Watmough,
3
Wyjątków nie można wyrzucić z bloków. Wyjątki zgłoszone w środowisku ARC mogą spowodować wyciek z twojego programu. Tak więc głosowanie negatywne.
Moszi
„nie ma znaczenia, jak drogie jest rzucanie i łapanie wyjątku” Piszę emulator, w którym wydajność ma kluczowe znaczenie. Nie mogę rzucić całej masy drogich wyjątków.
NobodyNada