Jak Mogę używać NSError w aplikacji na iPhone ' a?
Pracuję nad wyłapywaniem błędów w mojej aplikacji i szukam użycia NSError
. Jestem nieco zdezorientowany, jak go używać i jak go zaludnić.
Czy ktoś mógłby podać przykład jak wypełniam i używam NSError
?
8 answers
Cóż, to, co zwykle robię, to moje metody, które mogą usuwać błędy w czasie wykonywania, biorąc odniesienie do wskaźnika NSError
. Jeśli coś rzeczywiście pójdzie nie tak w tej metodzie, mogę wypełnić odniesienie NSError
danymi o błędzie i zwrócić nil z metody.
Przykład:
- (id) endWorldHunger:(id)largeAmountsOfMonies error:(NSError**)error {
// begin feeding the world's children...
// it's all going well until....
if (ohNoImOutOfMonies) {
// sad, we can't solve world hunger, but we can let people know what went wrong!
// init dictionary to be used to populate error object
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:@"ran out of money" forKey:NSLocalizedDescriptionKey];
// populate the error object with the details
*error = [NSError errorWithDomain:@"world" code:200 userInfo:details];
// we couldn't feed the world's children...return nil..sniffle...sniffle
return nil;
}
// wohoo! We fed the world's children. The world is now in lots of debt. But who cares?
return YES;
}
Możemy wtedy użyć tej metody. Nie trzeba nawet sprawdzać obiektu błędu, chyba że metoda zwróci nil:
// initialize NSError object
NSError* error = nil;
// try to feed the world
id yayOrNay = [self endWorldHunger:smallAmountsOfMonies error:&error];
if (!yayOrNay) {
// inspect error
NSLog(@"%@", [error localizedDescription]);
}
// otherwise the world has been fed. Wow, your code must rock.
Udało nam się uzyskać dostęp do błędu localizedDescription
, ponieważ ustawiliśmy wartość dla NSLocalizedDescriptionKey
.
Najlepszym miejscem na więcej informacji jest dokumentacja Apple. To naprawdę dobre.
Jest też fajny, prosty tutorial o Cocoa Is My Girlfriend .
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-12-28 05:36:39
Chciałbym dodać kilka sugestii na podstawie mojej najnowszej realizacji. Spojrzałem na jakiś kod od Apple i myślę, że mój kod zachowuje się w podobny sposób.
Powyższe posty już wyjaśniają, jak tworzyć obiekty NSError i zwracać je, więc nie będę się przejmował tą częścią. Postaram się zaproponować dobry sposób na integrację błędów (kodów, komunikatów) we własnej aplikacji.
Polecam stworzenie 1 nagłówka, który będzie przeglądem wszystkich błędów Twojego domena (np. aplikacja, biblioteka, itp..). Mój obecny nagłówek wygląda tak:
FSError.h
FOUNDATION_EXPORT NSString *const FSMyAppErrorDomain;
enum {
FSUserNotLoggedInError = 1000,
FSUserLogoutFailedError,
FSProfileParsingFailedError,
FSProfileBadLoginError,
FSFNIDParsingFailedError,
};
FSError.m
#import "FSError.h"
NSString *const FSMyAppErrorDomain = @"com.felis.myapp";
Teraz, gdy używasz powyższych wartości dla błędów, Apple utworzy podstawowy standardowy komunikat o błędzie dla Twojej aplikacji. Błąd można utworzyć w następujący sposób:
+ (FSProfileInfo *)profileInfoWithData:(NSData *)data error:(NSError **)error
{
FSProfileInfo *profileInfo = [[FSProfileInfo alloc] init];
if (profileInfo)
{
/* ... lots of parsing code here ... */
if (profileInfo.username == nil)
{
*error = [NSError errorWithDomain:FSMyAppErrorDomain code:FSProfileParsingFailedError userInfo:nil];
return nil;
}
}
return profileInfo;
}
Standardowy komunikat o błędzie generowany przez Apple (error.localizedDescription
) dla powyższego kodu będzie wyglądał następująco:
Error Domain=com.felis.myapp Code=1002 "The operation couldn’t be completed. (com.felis.myapp error 1002.)"
Powyższe jest już bardzo pomocne dla programisty, ponieważ komunikat wyświetla domenę, w której wystąpił błąd i odpowiadający mu kod błędu. Użytkownicy końcowi nie będą mieli pojęcia, co oznacza kod błędu 1002
, więc teraz musimy zaimplementować kilka ładnych komunikatów dla każdego kodu.
W przypadku komunikatów o błędach musimy pamiętać o lokalizacji(nawet jeśli nie implementujemy komunikatów zlokalizowanych od razu). W moim obecnym projekcie zastosowałem następujące podejście:
1) Utwórz plik strings
, który będzie zawierać błędy. Pliki ciągów można łatwo zlokalizować. Plik może wyglądać następująco:
FSError.stringi
"1000" = "User not logged in.";
"1001" = "Logout failed.";
"1002" = "Parser failed.";
"1003" = "Incorrect username or password.";
"1004" = "Failed to parse FNID."
2) Dodaj makra do konwersji kodów całkowitych do zlokalizowanych komunikatów o błędach. Użyłem 2 makra w moich stałych + makra.plik H. Zawsze dołączam ten plik do nagłówka prefiksu (MyApp-Prefix.pch
) dla wygody.
Stałe+Makra.h
// error handling ...
#define FS_ERROR_KEY(code) [NSString stringWithFormat:@"%d", code]
#define FS_ERROR_LOCALIZED_DESCRIPTION(code) NSLocalizedStringFromTable(FS_ERROR_KEY(code), @"FSError", nil)
3) Teraz łatwo jest pokazać przyjazny dla użytkownika komunikat o błędzie oparty na kod błędu. Przykład:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:FS_ERROR_LOCALIZED_DESCRIPTION(error.code)
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
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-19 14:38:58
Świetna odpowiedź Alex. Jednym z potencjalnych problemów jest dereferencja zerowa. Odsyłacz Apple do tworzenia i zwracania obiektów NSError
...
[details setValue:@"ran out of money" forKey:NSLocalizedDescriptionKey];
if (error != NULL) {
// populate the error object with the details
*error = [NSError errorWithDomain:@"world" code:200 userInfo:details];
}
// we couldn't feed the world's children...return nil..sniffle...sniffle
return nil;
...
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-05-10 20:11:58
Objective-C
NSError *err = [NSError errorWithDomain:@"some_domain"
code:100
userInfo:@{
NSLocalizedDescriptionKey:@"Something went wrong"
}];
Swift 3
let error = NSError(domain: "some_domain",
code: 100,
userInfo: [NSLocalizedDescriptionKey: "Something went wrong"])
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-11-07 18:06:45
Proszę zapoznać się z poniższym tutorialem
Mam nadzieję, że będzie to pomocne dla ciebie, ale wcześniej musisz przeczytać dokumentację NSError
Bardzo ciekawy link znalazłem ostatnio ErrorHandling
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-20 06:46:17
Spróbuję podsumować świetną odpowiedź Alexa i punkt jlmendezboniniego, dodając modyfikację, która sprawi, że wszystko będzie kompatybilne z ARC (na razie nie od ARC będzie narzekał, ponieważ powinieneś zwrócić id
, co oznacza "dowolny obiekt", ale BOOL
nie jest typem obiektu).
- (BOOL) endWorldHunger:(id)largeAmountsOfMonies error:(NSError**)error {
// begin feeding the world's children...
// it's all going well until....
if (ohNoImOutOfMonies) {
// sad, we can't solve world hunger, but we can let people know what went wrong!
// init dictionary to be used to populate error object
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:@"ran out of money" forKey:NSLocalizedDescriptionKey];
// populate the error object with the details
if (error != NULL) {
// populate the error object with the details
*error = [NSError errorWithDomain:@"world" code:200 userInfo:details];
}
// we couldn't feed the world's children...return nil..sniffle...sniffle
return NO;
}
// wohoo! We fed the world's children. The world is now in lots of debt. But who cares?
return YES;
}
Teraz zamiast sprawdzać wartość zwracaną wywołania metody, sprawdzamy czy error
jest nadal nil
. Jeśli nie, to mamy problem.
// initialize NSError object
NSError* error = nil;
// try to feed the world
BOOL success = [self endWorldHunger:smallAmountsOfMonies error:&error];
if (!success) {
// inspect error
NSLog(@"%@", [error localizedDescription]);
}
// otherwise the world has been fed. Wow, your code must rock.
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-12-31 01:50:50
Inny wzorzec projektowy, który widziałem, polega na użyciu bloków, co jest szczególnie przydatne, gdy metoda jest uruchamiana asynchronicznie.
Powiedzmy, że mamy zdefiniowane następujące kody błędów:
typedef NS_ENUM(NSInteger, MyErrorCodes) {
MyErrorCodesEmptyString = 500,
MyErrorCodesInvalidURL,
MyErrorCodesUnableToReachHost,
};
Zdefiniowałbyś swoją metodę, która może wywołać błąd w ten sposób:
- (void)getContentsOfURL:(NSString *)path success:(void(^)(NSString *html))success failure:(void(^)(NSError *error))failure {
if (path.length == 0) {
if (failure) {
failure([NSError errorWithDomain:@"com.example" code:MyErrorCodesEmptyString userInfo:nil]);
}
return;
}
NSString *htmlContents = @"";
// Exercise for the reader: get the contents at that URL or raise another error.
if (success) {
success(htmlContents);
}
}
A po wywołaniu nie musisz się martwić o zadeklarowanie obiektu NSError (wypełnienie kodu zrobi to za Ciebie) lub sprawdzenie zwracanej wartości. Możesz tylko dostarczyć dwa bloki: jeden, który będzie get called when there is an exception, and one that get called when it successful:
[self getContentsOfURL:@"http://google.com" success:^(NSString *html) {
NSLog(@"Contents: %@", html);
} failure:^(NSError *error) {
NSLog(@"Failed to get contents: %@", error);
if (error.code == MyErrorCodesEmptyString) { // make sure to check the domain too
NSLog(@"You must provide a non-empty string");
}
}];
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-07-08 03:44:50
Cóż jest to trochę poza zakresem pytań, ale w przypadku, gdy nie masz opcji dla NSError zawsze możesz wyświetlić błąd niskiego poziomu:
NSLog(@"Error = %@ ",[NSString stringWithUTF8String:strerror(errno)]);
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-02-14 14:18:58