2009-06-26 10 views
8

Koduję różne procedury i staram się jak najlepiej, aby zachować go w porządku i refaktoryzacji.Prawidłowa struktura do sprawdzenia pod kątem błędów za pomocą NSError

Metody Tworzę zaczynają wyglądać podobnie do tego kodu:

-(IBAction)buttonPress:(id)sender { 
    // Create Document Shopping List with this document 
    [self doSomething:&error]; 

    if(error) { 
     [NSApp presentError:&error]; 
     return nil; 
    } 

    [self doSomethingElse:&error]; 

    if(error) { 
     [NSApp presentError:&error]; 
     return nil; 
    } 

    [self doYetSomethingElse:&error]; 

    if(error) { 
     [NSApp presentError:&error]; 
     return nil; 
    } 
} 

Kocham NSError, ale to wydaje się być strasznie niezdarny sposób obsługi wszystkich moich błędów.

Kilka myśli ja miałem o alternatywnych sposobów:

a) sprawdzanie błędów może być wbudowany w metodach doSomething, doSomethingElse etc, ale wtedy nie byłby w stanie wyjść z metody Naciśnij przycisk bez sprawdzanie wartości zwracanej, co doprowadziłoby mnie do podobnej struktury.

b) Mogę ustawić NSError jako kluczową wartość obserwowaną, ale coś w tym jest głęboko błędne. Jestem bardzo świadomy możliwości nadużyć z KVO, więc staram się robić wszystko bez niego, gdzie tylko to możliwe.

Na pewno brakuje tu czegoś naprawdę podstawowego? Czy istnieje wzór, który może mi pomóc? A może ta struktura jest w porządku?

+0

Jestem absolutnie oszołomiony niesamowitą odpowiedzią, jaką otrzymałem w ciągu ostatnich 10 minut! Dziękuję bardzo za odpowiedzi. Bardzo, bardzo pomocny. –

Odpowiedz

11

Uważam, że żadna z przedstawionych tutaj odpowiedzi nie uwzględnia praktyk zalecanych przez Apple.

Zasadniczo nie należy nigdy sprawdzać, czy obiekt NSError określa, czy wystąpił problem. Sprawdzasz, czy wystąpił problem, sprawdzając wartość zwróconą przez metodę, która przenosi wskaźnik do wskaźnika na NSError.

Od the Apple documentation:

Ważne Sukces lub niepowodzenie jest wskazywana przez wartości zwracanej metody . Chociaż metody kakaowych, które pośrednio zwracają obiekty o błędach w domena błąd kakao są gwarantowane, aby powrócić takich obiektów, jeżeli metoda sygnalizuje awarię bezpośrednio powrocie nil lub NO, należy zawsze sprawdzić, zwracana jest wartość nil lub NO przed przystąpieniem do rób cokolwiek z obiektem NSError.

+0

Niektóre metody, takie jak sendAsynchronousRequest, zwracają puste miejsce i jedyne co możesz zrobić, to sprawdzić NSError. Czy może czegoś brakuje? –

+2

W takim przypadku sprawdź inne parametry. To znaczy NSURLResponse i NSData. –

0

Uwaga: Kiedy to opublikowałem, nie zdawałem sobie sprawy z żadnej debaty dotyczącej wyjątków MacOS/Cocoa. Proszę o tym pamiętać, rozważając tę ​​odpowiedź.

Zamiast tego należy użyć exceptions? To pozwoli ci zrezygnować z parametru błędu na twoich metodach i obsługiwać błędy w jednym miejscu.

-(IBAction)buttonPress:(id)sender { 
    // Create Document Shopping List with this document 
    @try { 
     [self doSomething]; 
     [self doSomethingElse]; 
     [self doYetSomethingElse]; 
    } @catch (NSException* e) { 
     [NSApp presentException:e]; 
    } 
    return nil; 
} 

Będziesz oczywiście trzeba rzucać wyjątki zgodnie z wymaganiami z poziomu metod doXXXX:

NSException* e = [NSException exceptionWithName:@"MyException" 
             reason:@"Something bad happened" 
             userInfo:nil]; 
@throw e; 
+3

Trwa debata na temat tego, czy używanie wyjątków do kontroli przepływu jest dopuszczalne w systemie Mac OS X/Cocoa. Osobiście zajmuję stanowisko, że (większość) Apple bierze, że wyjątki w kakao są wyjątkowe błędy programowania, a zatem nie powinny być wykorzystywane do kontroli przepływu. – danielpunkass

+0

Chciałbym wiedzieć, dlaczego to zostało zaznaczone. Co przeoczyłem? – teabot

+2

Ah, dzięki za oczyszczenie @danielpunkass - Pochodzę z tła Java, więc szczęśliwie używam wyjątków w Objective-C nieświadomych takiej debaty. – teabot

1

Myślę, że prawdziwe pytanie brzmi, ile trzeba ziarnistość w obsługę błędów. Twój kod jest zasadniczo poprawny - ale możesz czyścić rzeczy, sprawdzając błędy rzadziej, lub stosując podejście typu catch-all, takie jak teabot wspomniany w swojej odpowiedzi z NSException. Wiele wbudowanych funkcji, które zwracają błędy (np. MoveItemAtPath NSFileManagera) zwraca BOOL oprócz obiektu NSError - więc możesz również użyć zagnieżdżonych instrukcji if, jeśli nie potrzebujesz informacji NSError.

W sumie nie ma na to świetnej metody. Jeśli twój kod jest długi, KVO może być fajnym rozwiązaniem. Jednak nigdy nie próbowałem tego w takiej sytuacji.

+0

Dzięki za komentarz, Ben. Idealnie chciałbym móc obsłużyć wszystkie błędy w jeden spójny sposób - za pomocą NSError. Jestem nieco zdezorientowany przez funkcje, które czasami zwracają BOOL - jeśli istnieje standardowy sposób obsługi błędów za pomocą NSError, dlaczego Apple zabłociłoby wody, dostarczając również wartości zwracane? To mnie trochę zdezorientowało. –

+1

Opieranie się na obiekcie NSError jest, szczerze mówiąc, głupie. Bardzo łatwo jest pominąć ten jeden skrajny przypadek, w którym pisanie kodu w celu utworzenia obiektu błędu było zbyt trudne, a w rezultacie metoda błędnie informuje o powodzeniu. Dlatego firma Apple obsługuje informacje o błędach w taki sposób, w jaki postępują. Użyj prostego testu metody zwracającej NO lub zero, aby ustalić, że się nie udało. THEN opuść obiekt NSError (jeśli jest dostępny) po dalsze szczegóły. –

+0

Bardzo dobrze, Mike. Daje mi coś innego do rozważenia. Tylko to po prostu przeczytałem, nie mogłem wystarczająco szybko sprawdzić SO. –

2

Istotą twojego pytania jest to, czy możesz poprawić konstrukcję błędów w obsłudze błędów. Sądzę, że zasadniczo wprowadzając więcej warstw zagnieżdżania, albo poprzez wyodrębnienie większej ilości kodu do oddzielnych metod/funkcji, albo przez wprowadzenie zagnieżdżania w wysokiej przykładowej metodzie.

Chodzi o to, że jeśli chodzi o obsługę większości błędów, prawdopodobnie interesuje Cię wykonanie alternatywnego zadania lub awaria i propagowanie błędu w łańcuchu, aby jakiś odpowiedzialny kontroler mógł przekazać błąd użytkownikowi przez Interfejs użytkownika.

Stosując ten pomysł „propagują lub obsługiwać”, chciałbym przepisać metodę próbkowania tak:

-(IBAction)buttonPress:(id)sender { 

    // Create Document Shopping List with this document 
    [self doSomething:&error];  
    if(error == nil) { 
     [self doSomethingElse:&error]; 
     if (error == nil) { 
      [self doYetSomethingElse:&error]; 
     } 
    } 

    if(error) { 
     [NSApp presentError:&error]; 
    }  
} 

pamiętać, że istnieją dobre argumenty przeciwko wprowadzeniu zbyt dużo zagnieżdżania w danej metodzie. Zagnieżdżanie takie jak ta jest w zasadzie krótką alternatywą dla metod wyodrębniania. Może być na przykład bardziej sensowne, że "doSomething:" samo wywołuje polecenie "SomethingElse:", które wywołuje na przykład "YesetethingElse". To nałożyłoby tę samą strukturę na kod jak gniazdo-gniazdo, ale byłoby prawdopodobnie łatwiejsze w utrzymaniu.

Na marginesie, nie jestem fanem wbudowanych oświadczeń zwrotnych. W tym konkretnym przypadku przykładowa metoda faktycznie nie wymaga wartości zwracanej, ale jeśli tak, wolę ustawić zmienną lokalną na zwróconą wartość i powrócić tylko na końcu kontroli przepływu. Przeskakiwanie z funkcji lub metody przedwcześnie jest pewnym sposobem na poznanie dziwnych błędów, IMHO.

+0

Dzięki za doskonałą sugestię, Daniel. Odwróciłem się od tego, ponieważ Code Complete zaleca minimalizowanie zagnieżdżania. Zrobiłem kilka koszmarnych zagnieżdżonych, jeśli ... innych stwierdzeń w moim czasie. A gdybym miał więcej niż 5 kroków, myślę, że to byłoby kłopotliwe. Z drugiej strony, gdybym miał więcej niż 5 kroków, myślę, że byłby to znak ostrzegawczy, że i tak mój kod musi zostać zreaktowany. A to konkretne zagnieżdżanie jest wciąż czytelne i jest o wiele ładniejsze niż moje oryginalne, więc to właśnie zrobię. Jeszcze raz dziękuję! –

+1

Cieszę się, że doceniłeś odpowiedź. Myślę także o Code Complete i minimalizowaniu zagnieżdżania, ale racjonalizuję to tak, jak powyżej, aby uważać, że minimalne zagnieżdżanie jest tylko skrótem dla kodu, który można uwzględnić w innej metodzie lub funkcji, gdy wymagają tego okoliczności. Generalnie myślę, że zagnieżdżanie jest lepsze niż alternatywa, funkcja "run-on" ... – danielpunkass

Powiązane problemy