【问题标题】:Is testing for self still necessary and/or meaningful? [duplicate]自我测试是否仍然必要和/或有意义? [复制]
【发布时间】:2014-01-02 23:00:10
【问题描述】:

Objective-C 实例(例如,在指定的初始化程序中)中通常的初始化序列如下:

- (id)initWithFrame: (NSRect)frame {
  self = [super initWithFrame: frame];
  if (self != nil) {
    // Do your stuff.
  }
  return self;
}

这是一个众所周知的模式,但是否真的有必要测试 self 是否被赋值?我的意思是,如果在 super 方法中出现问题,与其只返回 nil,不如引发异常?这是一个安全的模式吗?如果对 super 的调用有问题但仍然返回(随机)指针怎么办?

这当然是防御性编程的问题,但是对于您确定永远不会有 nil 结果的案例进行如此多的测试看起来确实有些夸张。如果您知道它可能发生(必须记录在案),那么检查 self 当然很有意义。

【问题讨论】:

  • 很有可能,如果您返回 nil,您仍然会遇到异常,只是不会在 init 方法中。
  • 是的。遵守既定惯例。
  • 如果对super 的调用出现问题,然后它返回垃圾而不是nil 或有效指针,那么该代码中存在严重错误,应该鞭打开发人员。
  • 我刚刚注意到这是一个古老的讨论:stackoverflow.com/q/1287950/104790

标签: objective-c


【解决方案1】:

当出现问题时,在 Objective-C 程序中引发异常是不合理的行为。抛出异常应该只用于严重的、不可恢复的、程序员错误的情况。

从 init 方法发出错误信号的正确方法是返回 nil 并可选地用更多信息填充传递的 NSError 指针。测试您的超级初始化程序是否返回 nil 可防止您进行无用的初始化,并有助于避免可能的崩溃,例如:

@implementation { 
  int _foo; // instance variable
}

- (id)initWithFrame: (NSRect)frame {
  self = [super initWithFrame: frame];
  if (self != nil) {
    _foo = 2;
  }
  return self;
}

如果超级初始化器返回 nil,这将崩溃,因为 _foo = 2self->_foo = 2 的简写,它将取消引用 nil 指针。 (只有当 self 为 nil 时,消息才会返回 nil;直接访问 ivar 并不能保证。)

【讨论】:

  • 一个有力地说明返回 nil 而不是引发错误的事实是,Objective-C 有这种范例,您通常不需要检查 nil。也许初始化程序中的检查就是为此付出的代价?
  • 我认为这个问题与异常与返回 nil 无关。这两种情况都有正当的理由,在苹果的框架中有很多例子。重点是:这两种情况都必须记录在案并解释原因。如果有可能从超类返回nil,那么您显然不能省略条件。
【解决方案2】:

某些类,甚至 Apple 自己的 NS 类也可以从初始化程序返回 nil,例如NSArray 和 NSDictionary 的 initWithContentsOfFile:initWithContentsOfURL: 方法。那些应该抛出异常吗?不!它们是易于检测且易于恢复的病例。对于懒惰的程序员来说,抛出异常只不过是一个拐杖,他们懒得进行适当的错误检查,而对于我们这些这样做的人来说,这是一个麻烦。

【讨论】:

  • 在这些情况下,返回 nil 当然是有意义的,但这不是重点。如果不能为 nil,则检查 nil(我希望这远远超过可以返回 nil 的那些)。
【解决方案3】:

问题归结为:是否有任何(许多)已知类确实从初始化程序返回 nil

我认为很少。当然,有一些已知情况(构造函数通常通过引用获取 NSError)必须返回 nil 以指出错误。

但大多数其他初始化程序要么返回self 参数,要么返回该类的另一个实例。如果不是这种情况,则应在文档中明确表达,因为代码通常依赖于对象创建不会失败。

所以我认为在检查超类的行为时省略对nil 的测试是完全安全的。

另一方面,这种模式无处不在,以至于共同开发者可能会对遗漏感到困惑,并可能将其指出为疏忽。因此,如果您打算在没有条件的情况下切换到一种样式,至少应该以某种方式将其传达给您的开发人员。

【讨论】:

  • 这就是我的想法。对于永远不会返回 nil 的情况,有很多检查任务。有更好的方法来消耗 CPU 周期。
  • 我不确定我是否同意您的说法,即只有“少数”类返回 nil。例如,UIView 的指定初始化程序(initWithFrame:,如 OP 的问题)声明它可以返回 nil,因此任何 UIView 都可能从其 init 方法返回 nil。我认为这很常见,我认为假设没有这样的警告是保证初始化不会失败是不安全的。
  • @JesseRusak 然而,init 的大多数代码 outside 并不知道 initWithFrame: 可以返回 nil 的可能性,并且不可避免地会遇到 addSubview: 的异常:它抱怨零论点。如果没有立即进行 nil 检查,则应用程序通常仍无法按预期工作。因此,在 init 内部崩溃实际上可能是一件好事,并使调试更容易。
  • @JesseRusak 我刚刚提交了一个关于 initWithFrame: 返回 nil 的文档错误(雷达 15671536)。
  • @NikolaiRuhe 好的。我认为他们不会改变这一点,因为任何 init 方法都可以返回 nil 是一般规则,所以 initWithFrame 肯定是这样:。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-19
  • 2012-11-21
  • 2012-11-28
  • 1970-01-01
相关资源
最近更新 更多