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,设置err
,success == NO
。
没有缓存,但我们可以发出网络请求。 readCachedRequestReturningError:
会将err
设置为有效的NSErrorMYFileDomain, MYFileNotFoundCode
,但随后sendUncachedRequestReturningError:
将成功并设置success == YES
,并且根本不接触err
,将先前的错误留在其中。如果你现在检查err
而不是检查返回值,当一切顺利时你会认为有错误。
注意:上面的代码被大大简化了,因为我们只关心错误。当然,在实际程序中,这些方法可能会有另一个返回参数用于请求的实际回复,或者会返回回复或nil
,而不是success
BOOL
。它还可能检查err
是否为NULL
。
【讨论】:
err.domain
-> (*err).domain
,别忘了先检查NULL
。
好收获。我将留下NULL
-check out,因为这是一个简单的示例,但我同意在实际应用中您可能会将NSError**
标记为_Nullable
并检查。以上是关于Apple 检查返回值而不是错误的模式背后的基本原理是啥?的主要内容,如果未能解决你的问题,请参考以下文章