【问题标题】:How can I use NSError in my iPhone App?如何在我的 iPhone 应用程序中使用 NSError?
【发布时间】:2011-06-06 23:32:18
【问题描述】:

我正在处理我的应用程序中的错误,我正在考虑使用NSError。我对如何使用它以及如何填充它感到有些困惑。

有人可以提供我如何填充然后使用NSError 的示例吗?

【问题讨论】:

    标签: iphone ios objective-c ios4 nserror


    【解决方案1】:

    好吧,我通常做的是让我的可能在运行时出错的方法引用NSError 指针。如果该方法确实出了问题,我可以使用错误数据填充 NSError 引用并从该方法返回 nil。

    例子:

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

    然后我们可以使用这样的方法。除非方法返回 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.
    

    我们能够访问错误的localizedDescription,因为我们为NSLocalizedDescriptionKey 设置了一个值。

    了解更多信息的最佳地点是Apple's documentation。确实不错。

    Cocoa Is My Girlfriend 上还有一个不错的简单教程。

    【讨论】:

    • 这是有史以来最有趣的例子
    • 这是一个非常棒的答案,尽管在 ARC 中存在一些问题并将id 转换为BOOL。任何轻微的 ARC 兼容变化将不胜感激。
    • @TomJowett 如果仅仅因为 Apple 推动我们转向新的 ARC 世界,我们最终无法结束世界饥饿,我会非常生气。
    • 返回类型可以是BOOL。如果出现错误,则返回NO,而不是检查返回值,只需检查error。如果nil 继续,如果!= nil 处理它。
    • -1:您确实需要合并验证**error 不为零的代码。否则程序会抛出一个完全不友好的错误,并且不会让发生的事情变得明显。
    【解决方案2】:

    我想根据我最近的实施添加更多建议。我查看了一些来自 Apple 的代码,我认为我的代码的行为方式大致相同。

    上面的帖子已经解释了如何创建 NSError 对象并返回它们,所以我不会打扰那部分。我只是尝试提出一种在您自己的应用中集成错误(代码、消息)的好方法。


    我建议创建 1 个标头,用于概述您的域(即应用程序、库等)的所有错误。我当前的标题如下所示:

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

    现在,当使用上述错误值时,Apple 将为您的应用创建一些基本的标准错误消息。可能会产生如下错误:

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

    上述代码的标准 Apple 生成的错误消息 (error.localizedDescription) 如下所示:

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

    以上内容对开发人员已经很有帮助,因为消息显示了发生错误的域和相应的错误代码。最终用户将不知道错误代码1002 是什么意思,所以现在我们需要为每个代码实现一些漂亮的消息。

    对于错误消息,我们必须牢记本地化(即使我们不立即实施本地化消息)。我在当前项目中使用了以下方法:


    1) 创建一个包含错误的strings 文件。字符串文件很容易本地化。该文件可能如下所示:

    FSError.strings

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

    2) 添加宏以将整数代码转换为本地化错误消息。我在我的 Constants+Macros.h 文件中使用了 2 个宏。为方便起见,我总是将此文件包含在前缀标头 (MyApp-Prefix.pch) 中。

    常量+宏.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) 现在可以轻松地根据错误代码显示用户友好的错误消息。一个例子:

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

    【讨论】:

    • 很好的答案!但是为什么不把本地化的描述放在它所属的用户信息字典中呢? [NSError errorWithDomain:FSMyAppErrorDomain 代码:FSProfileParsingFailedError userInfo:@{NSLocalizedDescriptionKey : FS_ERROR_LOCALIZED_DESCRIPTION(error.code)}];
    • 有什么特别的地方应该放字符串文件吗?从 FS_ERROR_LOCALIZED_DESCRIPTION() 我得到的只是数字(错误代码)。
    • @huggie:不太清楚你的意思。我通常将我在整个应用程序中使用的这些宏放在一个名为 Constants+Macros.h 的文件中,并将此文件导入前缀标头(.pch 文件)中,以便它在任何地方都可用。如果您的意思是您只使用 2 个宏中的 1 个,那可能会起作用。也许从intNSString 的转换并不是真正必要的,尽管我还没有测试过。
    • @huggie:哦,我想我现在明白你了。字符串应该在一个可本地化的文件(.strings 文件)中,因为这是 Apple 宏的外观。在此处阅读有关使用 NSLocalizedStringFromTable 的信息:developer.apple.com/library/mac/documentation/cocoa/conceptual/…
    • @huggie:是的,我使用了本地化字符串表。宏FS_ERROR_LOCALIZED_DESCRIPTION 中的代码检查名为FSError.strings 的文件中的可本地化字符串。如果这对您来说不熟悉,您可能需要查看 Apple 关于 .strings 文件的本地化指南。
    【解决方案3】:

    很好的答案亚历克斯。一个潜在的问题是 NULL 取消引用。苹果对Creating and Returning NSError objects的引用

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

    【讨论】:

      【解决方案4】:

      Objective-C

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

      斯威夫特 3

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

      【讨论】:

        【解决方案5】:

        请关注tutorial

        我希望它对您有所帮助,但您必须先阅读 NSError 的文档

        这是我最近发现的非常有趣的链接ErrorHandling

        【讨论】:

          【解决方案6】:

          我将尝试总结 Alex 和 jlmendezbonini 的观点的最佳答案,添加一个修改以使所有 ARC 都兼容(到目前为止,ARC 不会抱怨,因为您应该返回 id,这意味着“任何对象” ,但BOOL 不是对象类型)。

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

          现在我们不再检查方法调用的返回值,而是检查error 是否仍然是nil。如果不是我们有问题。

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

          【讨论】:

          【解决方案7】:

          我看到的另一种设计模式涉及使用块,这在异步运行方法时特别有用。

          假设我们定义了以下错误代码:

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

          您将定义可能引发错误的方法,如下所示:

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

          然后当您调用它时,您无需担心声明 NSError 对象(代码完成会为您完成)或检查返回值。你可以只提供两个块:一个在异常时被调用,一个在成功时被调用:

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

          【讨论】:

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

            当我没有有效的错误对象时,我可以使用NSError.defaultError()

            let error = NSError.defaultError()
            print(error.localizedDescription) //Something went wrong.
            

            【讨论】:

              【解决方案9】:

              好吧,这有点超出问题范围,但如果您没有 NSError 选项,您总是可以显示低级错误:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-09-02
                • 1970-01-01
                • 2010-09-12
                • 2021-07-21
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多