ChatGPT解决这个技术问题 Extra ChatGPT

如何在我的 iPhone 应用程序中使用 NSError?

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

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


C
Cœur

好吧,我通常做的是让我的可能在运行时出错的方法引用一个 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 不为零的代码。否则,程序将抛出一个完全不友好的错误,并且不会使正在发生的事情变得明显。
W
Wolfgang Schreurs

我想根据我最近的实施添加更多建议。我查看了 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 的文件中的可本地化字符串。如果您对 .strings 文件不熟悉,您可能需要查看 Apple 的本地化指南。
j
jlmendezbonini

很好的答案亚历克斯。一个潜在的问题是 NULL 取消引用。 Apple 在 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;
...

A
AlBeebe

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"])

N
NANNAV

请参考以下tutorial

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

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


G
Gabriele Petronella

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

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

@Gabriela:Apple 声明当使用间接变量返回错误时,方法本身应该总是有一些返回值,以防成功或失败。 Apple 敦促开发人员首先检查返回值,并且在返回值因某种原因无效时检查错误。请参阅以下页面:developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…
S
Senseful

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

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

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

H
Hemang
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.

M
Mike.R

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

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