在Objective-C中,异常/返回NO/nil的最佳实践是什么?

Max*_*kov 22 exception-handling exception objective-c return-value

我是Objective-C的新手,我看到有关于错误处理的不同约定.有一些例外,但也有一些情况,如果出现问题,函数应该返回nil.

那么,我如何决定何时使用哪个,以及如何处理异常和意外的返回值?什么是最佳做法和危险信号?

jtb*_*des 36

我不会确定使用哪个,但这里有关于每个选项的一些信息:

例外

Obj-C中的例外并不真正用于控制程序流程.从有关异常处理文档:

一般模式是异常仅为程序员错误保留,并且捕获此类异常的程序应该很快就会退出.

出于这个原因,我不建议使用例外@try/ @catch仅测试方法是否正常工作.

除了设置更高级别的未捕获异常处理程序之外,您还有几个处理异常的选项.

错误

错误通常以三种方式使用:

委托方法

对象可以在指定的错误处理回调中简单地将NSError传递给其委托:

- (void)myObject:(MyObject *)obj didFailWithError:(NSError *)error;
Run Code Online (Sandbox Code Playgroud)

然后代表可以自由地采取任何适当的行动,包括可能向用户显示消息.此模式通常用于基于异步委托的API.

输出参数

这些最常用于布尔返回值:如果返回值为NO,则可以检查NSError对象以获取有关错误的更多信息.

- (BOOL)performTaskWithParameter:(id)param returningError:(out NSError **)error;
Run Code Online (Sandbox Code Playgroud)

一种可能的使用模式是:

NSError *error;
if (![myObject performTaskWithParameter:@"param" returningError:&error]) {
    NSLog(@"Task failed with error: %@", error);
}
Run Code Online (Sandbox Code Playgroud)

(有些人还喜欢在检查之前将布尔结果存储在变量中,例如BOOL success = [myObject perform...];.)由于此模式的线性特性,它最适合用于同步任务.

基于块的完成处理程序

自引入块以来的一个相当新的模式,但是非常有用的模式:

- (void)performAsynchronousTaskWithCompletionHandler:(void (^)(BOOL success, NSError *error))handler;
Run Code Online (Sandbox Code Playgroud)

像这样使用:

[myObject performAsynchronousTaskWithCompletionHandler:^(BOOL success, NSError *error) {
    if (!success) {
        // ...
    }
}];
Run Code Online (Sandbox Code Playgroud)

这变化很大:有时你不会看到布尔参数,只看错误; 有时处理程序块没有传递给它的参数,你只需检查对象的状态属性(例如,这是AVAssetExportSession的工作方式).当您需要基于块的方法时,此模式对于异步任务也很有用.

处理错误

Mac OS X上的Cocoa有一个非常彻底的错误处理路径.还有NSAlert的便利方法+ (NSAlert *)alertWithError:(NSError *)error;.在iOS上,NSError类仍然存在,但是没有相同的便捷方法来处理错误.您可能必须自己做很多事情.

阅读错误处理编程指南以获取更多信息.

归零

这通常与NSError out参数一起使用; 例如,NSData的方法

+ (id)dataWithContentsOfFile:(NSString *)path
                     options:(NSDataReadingOptions)mask
                       error:(NSError **)errorPtr;
Run Code Online (Sandbox Code Playgroud)

如果读取文件失败,则此方法返回nil,并且更多信息存储在错误中.

这是一个特别方便的模式的一个原因是因为nil消息传递,这可以安全地完成而在Obj-C中没有效果.我不会在这里详细说明为什么这是有用的,但你可以在其他地方的互联网上阅读更多关于它的内容.(只要确保找到一篇最新的文章;它曾经是返回浮点值的方法在发送到nil时不一定会返回0,但现在它们会这样做,如文档中所述.)


Wol*_*urs 6

在Objective-C中应尽可能少地使用例外.在其他语言使用异常的情况下,在Objective-C中,建议大多数时候使用NSError对象.

有关异常处理的Apple文档如下:http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/Exceptions/Exceptions.html%23//apple_ref/doc/uid/10000012il

那么如何使用NSError对象呢?好吧,如果我们查看Apple的类,使用间接指针返回错误.

例如:

- (NSObject *)objectFromSet:(NSSet *)set error:(NSError **)error 
{
    // get an object from a set; if the set has at least 1 object 
    // we return an object, otherwise an error is returned.

    NSObject *object = [set anyObject]
    if (!object) 
    {
         *error = [NSError errorWithDomain:@"AppDomain" code:1000 userInfo:nil];
         return nil;
    }

    return object;
}

// and then we use the function like this
- (void)test
{
    NSError *error = nil;
    NSSet *set = [[[NSSet alloc] init] autorelease];
    NSObject *object = [self objectFromSet:set error:&error];
    if (object) 
    {
        // use the object, all went fine ...
    }
    else 
    {
        // handle error, perhaps show an alert view ...
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 概念上正确,但代码严重错误.首先,在进行赋值之前,需要检查`objectFromSet:error:`中的`error`是否为非NULL.其次,你应该**从不测试错误来检查错误**.您**必须**检查返回值,并且*仅当nil*您考虑错误的内容时.(另外,没有要求在调用者中将`error`初始化为`nil`.) (5认同)
  • 有关测试`NSError` out-param的实际问题的示例,请参阅此答案http://stackoverflow.com/questions/2069039/error-handling-with-nsurlconnection-sendsynchronousrequest/2511161#2511161(我很惊讶接受这个问题的答案是错的) (2认同)