İPhone Uygulamamda NSError'u nasıl kullanabilirim?


228

Uygulamamdaki hataları yakalamak için çalışıyorum ve kullanmayı düşünüyorum NSError. Nasıl kullanılacağı ve nasıl doldurulacağı konusunda biraz kafam karıştı.

Biri nasıl doldurup nasıl kullandığım konusunda bir örnek verebilir NSErrormi?

Yanıtlar:


473

Peki, genellikle yaptığım zaman içinde hata-out bir NSErrorişaretçi bir referans almak olabilir benim yöntemler var . Bu yöntemde bir şey gerçekten yanlış giderse, NSErrorreferansı hata verileriyle doldurabilir ve yöntemden sıfır döndürebilirim.

Misal:

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

Daha sonra böyle bir yöntemi kullanabiliriz. Yöntem nil döndürmediği sürece hata nesnesini incelemeyi bile zahmet etmeyin:

// 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.

İçin localizedDescriptionbir değer ayarladığımız için hatalara erişebildik NSLocalizedDescriptionKey.

Daha fazla bilgi için en iyi yer Apple'ın belgeleridir . Gerçekten iyi.

Ayrıca Cocoa Is My Girlfriend hakkında güzel, basit bir öğretici var .


37
Bu en komik örnek, şimdiye kadar
ming yeow

Orada ARC bazı sorunlar vardır ve döküm rağmen bu, oldukça harika bir cevaptır ida BOOL. Hafif ARC uyumlu varyasyonlar çok takdir edilecektir.
NSTJ

6
@TomJowett Eğer dünya açlığını sonlandıramıyor olursak gerçekten çok sinirlenirdim çünkü Apple bizi sadece yeni ARC dünyasına taşınmaya itti.
Manav

1
dönüş türü olabilir BOOL. NOHata durumunda geri dönün ve dönüş değerini kontrol etmek yerine kontrol edin error. Eğer nildevam edersen , != nilhalledersen.
Gabriele Petronella

8
-1: Gerçekten **errornil olmadığını doğrulayan bir kod eklemeniz gerekir . Aksi takdirde, program tamamen düşmanca bir hata atar ve ne olduğunu açıkça göstermez.
FreeAsInBeer

58

En son uygulamamı temel alarak daha fazla öneri eklemek istiyorum. Apple'ın bazı kodlarına baktım ve kodumun aynı şekilde davrandığını düşünüyorum.

Yukarıdaki gönderiler zaten NSError nesnelerini nasıl oluşturacağınızı ve onları nasıl geri getireceğinizi açıklıyor, bu yüzden bu bölümle uğraşmayacağım. Sadece kendi uygulamanızdaki hataları (kodlar, mesajlar) entegre etmek için iyi bir yol önermeye çalışacağım.


Alan adınızdaki tüm hatalara (uygulama, kitaplık vb.) Genel bir bakış olacak 1 başlık oluşturmanızı öneririz. Geçerli başlığım şöyle görünüyor:

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";

Şimdi yukarıdaki hatalar için değerleri kullanırken, Apple uygulamanız için bazı temel standart hata mesajları oluşturacaktır. Aşağıdaki gibi bir hata oluşturulabilir:

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

error.localizedDescriptionYukarıdaki kod için Apple tarafından oluşturulan standart hata mesajı ( ) aşağıdaki gibi görünecektir:

Error Domain=com.felis.myapp Code=1002 "The operation couldn’t be completed. (com.felis.myapp error 1002.)"

Yukarıdaki mesaj, bir geliştirici için zaten oldukça yararlıdır, çünkü mesaj hatanın meydana geldiği alanı ve ilgili hata kodunu görüntüler. Son kullanıcıların hata kodunun ne 1002anlama geldiğine dair hiçbir fikri olmayacak , bu yüzden şimdi her kod için bazı güzel mesajlar uygulamalıyız.

Hata iletileri için yerelleştirmeyi akılda tutmamız gerekir (yerelleştirilmiş iletileri hemen uygulamasak bile). Mevcut projemde şu yaklaşımı kullandım:


1) stringshataları içerecek bir dosya oluşturun . Dizeler dosyaları kolayca yerelleştirilebilir. Dosya aşağıdaki gibi görünebilir:

FSError.strings

"1000" = "User not logged in.";
"1001" = "Logout failed.";
"1002" = "Parser failed.";
"1003" = "Incorrect username or password.";
"1004" = "Failed to parse FNID."

2) Tamsayı kodlarını yerelleştirilmiş hata mesajlarına dönüştürmek için makrolar ekleyin. Sabitler + Macros.h dosyamda 2 makro kullandım. MyApp-Prefix.pchKolaylık sağlamak için bu dosyayı her zaman önek başlığına ( ) eklerim.

Sabitler + Macros.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) Artık bir hata koduna dayalı kullanıcı dostu bir hata mesajı göstermek kolaydır. Bir örnek:

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" 
            message:FS_ERROR_LOCALIZED_DESCRIPTION(error.code) 
            delegate:nil 
            cancelButtonTitle:@"OK" 
            otherButtonTitles:nil];
[alert show];

9
Mükemmel cevap! Ancak neden yerelleştirilmiş açıklamayı ait olduğu kullanıcı bilgi sözlüğüne koymuyorsunuz? [NSError errorWithDomain: FSMyAppErrorDomain kodu: FSProfileParsingFailedError userInfo: @ {NSLocalizedDescriptionKey: FS_ERROR_LOCALIZED_DESCRIPTION (hata.code)}];
Richard Venable

1
Dize dosyasını koymam gereken belirli bir yer var mı? FS_ERROR_LOCALIZED_DESCRIPTION () sadece sayı (hata kodu) alıyorum.
huggie

@huggie: ne demek istediğinden emin değilim. Genellikle bu makroyu tüm uygulama boyunca adlı bir dosyaya koyarım Constants+Macros.hve bu dosyayı önek başlığına ( .pchdosya) alır, böylece her yerde kullanılabilir. 2 makrodan yalnızca birini kullandığınızı kastediyorsanız, bu işe yarayabilir. Belki dönüşüm intiçin NSStringbu test değil de, gerçekten gerekli değildir.
Wolfgang Schreurs

@huggie: ow, sanırım şimdi anlıyorum. Dizelerin yerelleştirilebilir bir dosyada ( .stringsdosya) olması gerekir , çünkü Apple'ın makrosu burada görünür. NSLocalizedStringFromTableBurada kullanma hakkında bilgi edinin : developer.apple.com/library/mac/documentation/cocoa/conceptual/…
Wolfgang Schreurs

1
@huggie: Evet, yerelleştirilmiş dize tablolarını kullandım. FS_ERROR_LOCALIZED_DESCRIPTIONMakrodaki kod adlı bir dosyada yerelleştirilebilir dizeyi denetler FSError.strings. Bu .stringssizin için yabancıysa, Apple'ın dosyalardaki yerelleştirme kılavuzuna göz atmak isteyebilirsiniz .
Wolfgang Schreurs

38

Harika cevap Alex. Potansiyel konulardan biri NULL dereference. Apple'ın NSError nesneleri Oluşturma ve İade Etme referansı

...
[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;
...

30

Objective-C

NSError *err = [NSError errorWithDomain:@"some_domain"
                                   code:100
                               userInfo:@{
                                           NSLocalizedDescriptionKey:@"Something went wrong"
                               }];

Hızlı 3

let error = NSError(domain: "some_domain",
                      code: 100,
                  userInfo: [NSLocalizedDescriptionKey: "Something went wrong"])

9

Lütfen aşağıdaki eğiticiye bakın

umarım sizin için yararlı olacaktır ama önce NSError belgelerini okumak zorundasınız

Bu son zamanlarda bulduğum çok ilginç bir bağlantı ErrorHandling


3

Alex ve jlmendezbonini'nin büyük cevabını özetlemeye çalışacağım, her şeyi ARC uyumlu hale getirecek bir değişiklik ekleyeceğim (şimdiye kadar ARC şikayet edeceğinizden beri dönmeyecek id, "herhangi bir nesne" anlamına geliyor, ancak BOOLbir nesne değil) ) kullanımı önerilir.

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

Şimdi yöntem çağrımızın dönüş değerini kontrol etmek yerine error, hala olup olmadığını kontrol ediyoruz nil. Eğer değilse, bir sorunumuz var.

// 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.

3
@Gabriela: Apple, hataları döndürmek için dolaylı değişkenleri kullanırken, yöntemin başarı veya başarısızlık durumunda her zaman bir dönüş değerine sahip olması gerektiğini belirtir. Apple, geliştiricilere önce dönüş değerini ve yalnızca dönüş değeri bir şekilde geçersiz hata olup olmadığını kontrol etmelerini önerir. Aşağıdaki sayfaya bakın: developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…
Wolfgang Schreurs

3

Gördüğüm başka bir tasarım deseni, özellikle bir yöntem eşzamansız olarak çalıştırıldığında yararlı olan blokları kullanmayı içerir.

Aşağıdaki hata kodlarının tanımlandığını varsayalım:

typedef NS_ENUM(NSInteger, MyErrorCodes) {
    MyErrorCodesEmptyString = 500,
    MyErrorCodesInvalidURL,
    MyErrorCodesUnableToReachHost,
};

Aşağıdaki gibi bir hata oluşturabilecek yönteminizi tanımlarsınız:

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

Ve sonra onu çağırdığınızda, NSError nesnesini bildirme (kod tamamlama sizin için yapacak) veya dönen değeri kontrol etme konusunda endişelenmenize gerek yok. Sadece iki blok sağlayabilirsiniz: biri istisna olduğunda çağrılacak, diğeri başarılı olduğunda çağrılacak:

[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");
    }
}];

0

Biraz soru kapsamı dışında ama NSError için bir seçeneğiniz yoksa her zaman Düşük seviye hatasını görüntüleyebilirsiniz:

 NSLog(@"Error = %@ ",[NSString stringWithUTF8String:strerror(errno)]);

0
extension NSError {
    static func defaultError() -> NSError {
        return NSError(domain: "com.app.error.domain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Something went wrong."])
    }
}

NSError.defaultError()geçerli bir hata nesnesi olmadığında kullanabilirsiniz .

let error = NSError.defaultError()
print(error.localizedDescription) //Something went wrong.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.