rzucanie wyjątku w objective-c / cocoa

Jaki jest najlepszy sposób na rzucenie wyjątku w objective-C / cocoa?

Author: Pramod More, 2008-11-27

13 answers

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

[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
 515
Author: e.James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-23 18:18:43

Słowo przestrogi. W Objective-C, w przeciwieństwie do wielu podobnych języków, powinieneś unikać używania wyjątków dla typowych sytuacji błędów, które mogą wystąpić podczas normalnego działania.

Dokumentacja Apple dla Obj-C 2.0 stwierdza, co następuje:"ważne: wyjątki są zasobochłonne w Objective-C. nie należy używać WYJĄTKÓW do ogólnej kontroli przepływu lub po prostu do oznaczania błędów (np. plik nie jest dostępny)"

Koncepcyjny Apple Dokumentacja obsługi wyjątków wyjaśnia to samo, ale więcej słów: "ważne: należy zarezerwować używanie WYJĄTKÓW do programowania lub nieoczekiwanych błędów runtime, takich jak dostęp do kolekcji poza granicami, próby mutacji niezmiennych obiektów, wysłanie nieprawidłowej wiadomości i utrata połączenia z serwerem okien. Zazwyczaj tego rodzaju błędy są usuwane z wyjątkiem sytuacji, gdy aplikacja jest tworzona,a nie w czasie wykonywania. [.....] Zamiast WYJĄTKÓW, błąd obiekty (NSError) i mechanizm dostarczania błędów Cocoa są zalecanym sposobem komunikowania oczekiwanych błędów w aplikacjach Cocoa."

Powodem tego jest po części przestrzeganie idiomów programistycznych w Objective - C (używanie wartości zwracanych w prostych przypadkach i parametrów by-reference (często Klasa NSError) w bardziej złożonych przypadkach), po części to, że rzucanie i łapanie wyjątków jest znacznie droższe i wreszcie (i co najważniejsze, wprawia w zakłopotanie), że wyjątki Objective-C są cienkim opakowaniem wokół funkcji setjmp () i longjmp() w C, zasadniczo psujących ostrożną obsługę pamięci, zobacz to Wyjaśnienie .

 252
Author: harms,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-07 10:18:02
@throw([NSException exceptionWith…])
 59
Author: Peter Hosey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-27 18:35:25

Nie mam reputacji, aby skomentować odpowiedź ejamesa, więc chyba muszę umieścić swoją tutaj. W przypadku osób wywodzących się z środowiska Java należy pamiętać, że Java rozróżnia wyjątki od RuntimeException. Wyjątek jest wyjątkiem zaznaczonym, a wyjątek RuntimeException nie jest zaznaczony. W szczególności Java sugeruje użycie WYJĄTKÓW sprawdzonych dla "normalnych warunków błędu" i WYJĄTKÓW niezaznaczonych dla " błędów uruchomieniowych spowodowanych błędem programisty."Wydaje się, że należy stosować wyjątki Objective-C w tych samych miejscach można użyć wyjątku niezaznaczonego, a wartości zwracane przez kod błędu lub wartości NSError są preferowane w miejscach, w których można użyć wyjątku zaznaczonego.

 33
Author: Daniel Yankowsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-08 14:24:36

Myślę, że aby być konsekwentnym, lepiej jest użyć @ throw z własną klasą, która rozszerza NSException. Następnie używasz tych samych notacji dla try catch finally:

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

Apple wyjaśnia tutaj, jak rzucać i obsługiwać wyjątki: Wyłapywanie WYJĄTKÓW Rzucanie WYJĄTKÓW

 15
Author: rustyshelf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-27 22:23:10

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 "darmowy", ale rzucanie i łapanie wyjątków jest znacznie droższe.

W każdym razie, twierdzenia (używając rodziny makr NSAssert i nscassert) rzucają NSException, a to rozsądne, aby używać ich jako Stanów Ries.

 14
Author: Psycho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-06 21:39:59

Użyj NSError do komunikowania błędów, a nie WYJĄTKÓW.

Szybkie punkty o NSError:

  • NSError pozwala kodom błędów w stylu C (liczbom całkowitym) wyraźnie zidentyfikować główną przyczynę i, miejmy nadzieję, umożliwić obsłudze błędów przezwyciężenie 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 na bardziej szczegółowe opisanie błędu z członkiem słownika userInfo.

  • Ale co najlepsze, NSError nie może zostać wyrzucony, więc 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 dalej i dalej w górę stosu wywołań, w którym to momencie można tylko zgłosić użytkownikowi i nie obsługiwać go w żaden znaczący sposób(nie jeśli wierzysz w przestrzeganie największej Zasady OOP ukrywania informacji).

Odnośnik: odniesienie

 8
Author: Jason Fuerstenberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-18 08:38:48

Tak nauczyłem się tego z "The Big Nerd Ranch Guide (4th edition)":

@throw [NSException exceptionWithName:@"Something is not right exception"
                               reason:@"Can't perform this operation because of this or that"
                             userInfo:nil];
 7
Author: Johannes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-12 07:33:34

Możesz użyć dwóch metod do wywołania wyjątku w bloku try catch

@throw[NSException exceptionWithName];

Lub druga metoda

NSException e;
[e raise];
 6
Author: Subbu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-23 11:04:28

Uważam, że nigdy nie należy używać WYJĄTKÓW do kontrolowania normalnego przepływu programu. Ale wyjątki powinny być rzucane, 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ć równa nil, to dobrze jest usunąć wyjątek zamiast próbować zrobić coś "mądrego"...

Ries

 3
Author: R. van Twisk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-17 19:42:42

Możesz rzucać wyjątki tylko wtedy, gdy znajdziesz się w sytuacji, która wskazuje na błąd programowania i chcesz zatrzymać działanie aplikacji. Dlatego najlepszym sposobem na wyrzucenie wyjątków jest użycie makr NSAssert i NSParameterAssert i upewnienie się, że ns_block_assertions nie jest zdefiniowane.

 0
Author: gnasher729,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-30 09:01:35

Przykładowy kod dla przypadku: @ throw ([NSException exceptionWithName:...

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

}

Użycie:

[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

}

}

 0
Author: Aleksandr B.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-09 11:32:31

Nie ma powodu, aby nie używać WYJĄTKÓW normalnie w objective C nawet do oznaczania wyjątków od reguł biznesowych. Apple może powiedzieć użyj NSError kogo to obchodzi. Obj C istnieje od dawna i w jednym czasie cała dokumentacja C++ mówi to samo. Powodem, dla którego nie ma znaczenia, jak kosztowne jest rzucanie i łapanie wyjątku, jest żywotność wyjątku jest wyjątkowo krótka i...to wyjątek od normalnego przepływu. Nigdy w życiu nie słyszałem, żeby ktoś mówił, że ten wyjątek wziął długo trzeba było rzucać i łapać.

Są też ludzie, którzy uważają, że samo objective C jest zbyt drogie i zamiast tego kodują w C lub c++. Więc mówienie, że zawsze używaj NSError jest źle poinformowane i paranoiczne.

Ale na pytanie tego wątku nie udzielono jeszcze odpowiedzi jaki jest najlepszy sposób na wyrzucenie wyjątku. Sposoby zwracania NSError są oczywiste.

Tak jest: [NSException raise:... @throw [[NSException alloc] initWithName.... lub @ throw [[MyCustomException... ?

Używam tutaj Zasady checked/unchecked nieco inaczej niż powyżej.

Rzeczywista różnica pomiędzy zaznaczonymi/niezaznaczonymi (używając tu metafory Javy) jest ważna -- > czy można odzyskać wyjątek. A przez odzyskiwanie mam na myśli nie tylko nie crash.

Więc używam niestandardowych klas WYJĄTKÓW z @ throw do odzyskiwania wyjątków, ponieważ prawdopodobnie będę miał jakąś metodę aplikacji, która szuka pewnych typów awarii w wielu @ catch blocks. Na przykład, jeśli mój aplikacja to bankomat, ja bym miał blok @ catch na "Wycofanie żądania usunięcia balanceexception".

Używam NSException: raise dla WYJĄTKÓW runtime, ponieważ nie mam możliwości odzyskania z wyjątku, poza tym, żeby złapać go na wyższym poziomie i zalogować. I nie ma sensu tworzyć do tego niestandardowej klasy.

W każdym razie to właśnie robię, ale jeśli jest lepszy, podobnie wyrazisty sposób, to też chciałbym wiedzieć. W moim własnym kodzie, ponieważ przestałem kodować C a hella dawno temu I nigdy nie zwracaj NSError, nawet jeśli został mi przekazany przez API.

 -7
Author: deleted_user,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 23:07:44