Apple 检查返回值而不是错误的模式背后的基本原理是啥?

Posted

技术标签:

【中文标题】Apple 检查返回值而不是错误的模式背后的基本原理是啥?【英文标题】:What is the rationale behind Apple's pattern of checking return value rather than error?Apple 检查返回值而不是错误的模式背后的基本原理是什么? 【发布时间】:2019-01-18 10:37:58 【问题描述】:

Apple 的Using and Creating Error Objects 指南提供了以下代码示例:

NSError *theError;
BOOL success = [myDoc writeToURL:[self docURL] ofType:@"html" error:&theError];

if (success == NO) 
    // Maybe try to determine cause of error and recover first.
    NSAlert *theAlert = [NSAlert alertWithError:theError];
   [theAlert runModal]; // Ignore return value.

并附上声明:

重要提示:成功或失败由方法的返回值指示。虽然间接返回 Cocoa 错误域中的错误对象的 Cocoa 方法如果通过直接返回 nil 或 NO 指示失败,则保证返回此类对象,但在尝试对NSError 对象。

我一直想知道为什么这种模式如此重要?为什么我们总是要检查返回值?如果我们检查 error 是否为 nil 有什么问题?

【问题讨论】:

【参考方案1】:

这种设计并不是很不寻常,也可以比较标准 C 中的 errno。

该设计具有许多潜在优势:

函数不必通过指针写入成功。这不仅使此类函数的实现更容易且不易出错,而且还可以带来很小的性能优势(例如,如果函数成功,这可以防止 CPU 缓存失效)。

如果我们总是在访问错误之前检查函数是否失败,我们可以将相同的错误指针用于多个函数。否则,我们可能会遇到之前的失败,而不是最近的函数失败。

这使得验证代码更容易编写。例如。默认情况下,函数可以设置错误。如果所有验证都通过,该函数可以简单地返回成功,而不必重置错误变量。

一个函数在调用其他函数时可以使用相同的错误指针,但是这些帮助函数的失败并不一定意味着顶层函数的失败。

在您的具体情况下,变量NSError *theError; 尚未初始化。访问该变量而不首先分配它会调用未定义的行为。文档仅保证在发生错误时将设置变量。

【讨论】:

还有;计算误差可能很昂贵。如果调用者只需要知道成功/失败,那么它可以为错误参数传递 NULL 并且被调用者可以知道不必费心计算错误。 关于未初始化变量的最终评论仅适用于 ARC 之前的代码(并且引用的 Apple 指南最初是在 ARC 之前的时代编写的)。在ARC代码中,默认多年,变量会自动初始化为nil【参考方案2】:

想象一下你用其他几个方法来实现一个方法:

-(BOOL)sendCachedRequestReturningError: (NSError**)err 
    BOOL success = [self readCachedRequestReturningError:err];
    if (!success && (*err).domain == MYFileDomain && (*err).errorCode == MYFileNotFoundCode) 
        success = [self sendUncachedRequestReturningError:err];
    

    return success;

现在这里有4条代码路径:

    有一个缓存请求。我们将返回success == YES,一切都很好。 尝试从缓存中读取时发生不可恢复的错误。 readCachedRequestReturningError: 将设置err 并设置success == NO,调用者将调用presentError: 或其他任何内容 尝试执行网络请求时出错。同#2,设置errsuccess == NO。 没有缓存,但我们可以发出网络请求。 readCachedRequestReturningError: 会将err 设置为有效的NSErrorMYFileDomain, MYFileNotFoundCode,但随后sendUncachedRequestReturningError: 将成功并设置success == YES,并且根本不接触err,将先前的错误留在其中。如果你现在检查err而不是检查返回值,当一切顺利时你会认为有错误。

注意:上面的代码被大大简化了,因为我们只关心错误。当然,在实际程序中,这些方法可能会有另一个返回参数用于请求的实际回复,或者会返回回复或nil,而不是successBOOL。它还可能检查err 是否为NULL

【讨论】:

err.domain -> (*err).domain,别忘了先检查NULL 好收获。我将留下NULL-check out,因为这是一个简单的示例,但我同意在实际应用中您可能会将NSError** 标记为_Nullable 并检查。

以上是关于Apple 检查返回值而不是错误的模式背后的基本原理是啥?的主要内容,如果未能解决你的问题,请参考以下文章

代码中存在数据类型错误,其中类型是布尔值而不是整数

对象的 LINQ 检查列表包含一个值而不是其他一些值

在javascript中分配值而不是引用[重复]

是否有SQL子句来检查是否存在所有多个值而不是使用多个AND?

如何使用变量的值而不是它的标识符?

猫鼬返回默认值而不是空值