在 Objective-C 中,为什么要检查 self = [ super init ]是否为 nil?

我有一个关于在 Objective-C 中编写 init 方法的一般问题。

我在任何地方(苹果的代码、书籍、开源代码等等)都看到 init 方法在继续初始化之前应该检查 self = [ super init ]是否为 nil。

Init 方法的默认 Apple 模板是:

- (id) init
{
self = [super init];


if (self != nil)
{
// your code here
}


return self;
}

为什么?

我的意思是 init 什么时候会返回零?如果我调用了 NSObject 的 init 却没有结果,那肯定是有什么事情搞砸了,对吧?那样的话,你还不如不写程序。

一个类的 init 方法可能返回 nil 真的那么常见吗? 如果是这样,在什么情况下,为什么?

40266 次浏览

这是为了检查初始化是否正常工作,如果 init 方法没有返回 nil,if 语句将返回 true,因此这是检查对象创建是否正常工作的一种方法。我认为 init 可能会失败的几个原因可能是它是一个超类不知道的重写的 init 方法或者类似的东西,我不认为它是那么常见。但是如果它真的发生了,最好什么也不要发生,而不是一个崩溃,我想所以它总是被检查..。

例如:

[[NSData alloc] initWithContentsOfFile:@"this/path/doesn't/exist/"];
[[NSImage alloc] initWithContentsOfFile:@"unsupportedFormat.sjt"];
[NSImage imageNamed:@"AnImageThatIsntInTheImageCache"];

诸如此类。(注意: 如果文件不存在,NSData 可能会抛出异常)。在很多领域,当出现问题时返回 nil 是预期的行为,因此为了保持一致性,几乎所有时间都检查 nil 是标准实践。

通常,如果类直接从 NSObject派生,则不需要。但是,这是一个好习惯,因为如果您的类从其他类派生,那么它们的初始化器可能返回 nil,如果是这样,那么您的初始化器就可以捕获它并正确地执行。

是的,郑重声明,我遵循最佳实践,并将它写在我所有的类上,即使是那些直接从 NSObject派生出来的类。

您是对的,您通常可以只编写 [super init],但是这不适用于任何类的子类。人们更喜欢只记住一行标准代码,并且一直使用它,即使有时候它只是必要的,因此我们得到了标准的 if (self = [super init]),它既考虑了返回 nil 的可能性,也考虑了返回除 self以外的对象的可能性。

这个特殊的习语是标准的,因为它在所有情况下都适用。

虽然不常见,但也会有..。

[super init];

... 返回一个不同的实例,因此需要赋值给 self。

在某些情况下,它会返回 nil,因此需要进行 nil 检查,这样你的代码就不会试图初始化一个不再存在的实例变量槽。

底线是,它是正确使用的文档模式,如果您没有使用它,那么您的做法就是错误的。

这是上面评论的总结。

假设超类返回 nil,会发生什么?

如果你不遵守传统

您的代码将在 init方法的中间崩溃(除非 init没有什么重要的内容)

如果您遵循惯例,不知道超类可能返回 null (大多数人最终会在这里结束)

您的代码可能会在以后的某个时候崩溃,因为您的实例是 nil,您期望的是不同的东西。否则你的程序会出乎意料的运行而不会崩溃。哦,天哪!你想要这个吗?我不知道..。

如果您遵循约定,愿意让您的子类返回 null

您的代码文档(!)应该清楚地说明: “ return... or nil”,其余的代码需要为处理这个问题做好准备。 现在说得通了。

写作是一个常见的错误

self = [[super alloc] init];

它返回超类的一个实例,这不是你想要的子类构造函数/init。 您得到一个不响应子类方法的对象,这可能会造成混淆,并产生关于不响应未找到的方法或标识符的混淆错误,等等。

self = [super init];

如果超类在设置子类的成员之前有成员(变量或其他对象)来初始化 第一,那么就需要。否则,objecc 运行时将它们全部初始化为 0没有。(与 ANSI C 不同,ANSI C 通常在分配内存块时根本不清除它们)

是的,由于内存不足错误、缺少组件、资源获取失败等原因,基类初始化可能会失败,因此检查是否为 nil 是明智的,只需要不到几毫秒的时间。

我认为,在大多数类中,如果[ super init ]的返回值是 nil,并且您按照标准实践的建议检查它,然后如果返回 nil,则过早返回,基本上您的应用程序仍然无法正常工作。 如果你认为它,即使如果(自我!= nil)检查是否存在,为了正确操作你的类,99.99% 的时间你实际上 需要自己是非零的。 现在,假设,不管出于什么原因,[ super init ] 是的返回 nil,基本上您对 nil 的检查是将责任传递给您的类的调用方,在这种情况下它很可能会失败,因为它会自然而然地假设调用是成功的。

基本上,我得到的是,99.99% 的时间,如果(自我!= nil)不会给您带来更大的健壮性,因为您只是将责任推给了调用者。要真正能够稳健地处理这个问题,实际上需要在整个调用层次结构中放入检查。即使这样,它唯一能买到的东西就是你的应用程序会失败得更干净/更健壮一些。但还是会失败。

如果一个库类任意决定返回 nil 作为[ super init ]的结果,那么无论如何你都会感到非常糟糕,这更多地表明库类的编写者在实现上犯了一个错误。

我认为这更多的是一个遗留的编码建议,当应用程序运行在更有限的内存。

但是对于 C 级代码,我通常仍然会根据 NULL 指针检查 malloc ()的返回值。然而,对于 Objective-C,在找到相反的证据之前,我想我通常会跳过 if (self!= 零)支票。为什么会这样?

因为,在 C 和 malloc 级别,在某些情况下,您实际上可以部分恢复。然而我认为在 Objective-C 中,在99.99% 的情况下,如果[ super init ]确实返回了 null,那么即使你试图处理它,你也基本上是完蛋了。你还不如让这个应用程序崩溃,然后处理后果。

在 OSX 中,由于内存的原因,-[NSObject init]不太可能失败。但 iOS 就不一样了。

另外,当子类化一个可能由于某种原因返回 nil的类时,这也是一个很好的编写实践。