Jaki jest najlepszy sposób na zgłoszenie wyjątku w celu c / kakao?
Jaki jest najlepszy sposób na zgłoszenie wyjątku w celu c / kakao?
Odpowiedzi:
Używam [NSException raise:format:]
w następujący sposób:
[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
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 .
@throw([NSException exceptionWith…])
Xcode rozpoznaje @throw
instrukcje jako punkty wyjścia funkcji, takie jak return
instrukcje. Użycie @throw
składni pozwala uniknąć błędnych ostrzeżeń „ Kontrola może osiągnąć koniec funkcji nieważnych ”, które możesz uzyskać [NSException raise:…]
.
Ponadto @throw
można go używać do rzucania obiektów, które nie należą do klasy NSException.
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.
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
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.
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
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
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.
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
}
}
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.
@throw([NSException exceptionWith…])
podejścia, ponieważ jest bardziej zwięzły.