关于null:在Objective-C中我为什么要检查self = [super init]是不是nil?

In Objective-C why should I check if self = [super init] is not nil?

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

在继续初始化之前,init方法应该检查self=[super init]是否不为零。

init方法的默认Apple模板是:

1
2
3
4
5
6
7
8
9
10
11
- (id) init
{
    self = [super init];

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

    return self;
}

为什么?

我的意思是init什么时候能把零还回来?如果我在nsobject上打电话给init,却又得到了nil,那么一定有什么事情真的搞砸了,对吧?在这种情况下,你最好不要写程序…

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


例如:

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

…等等。(注意:如果文件不存在,nsdata可能会引发异常)。在很多领域,当出现问题时,返回零是预期的行为,因此,为了一致性,经常检查零是标准做法。


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

虽然不常见,但也有一些情况…

1
[super init];

…返回另一个实例,因此需要将分配给self。

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

归根结底,它是一个文档化的正确模式,如果您不使用它,那么您就错了。


我认为,在大多数类中,如果[super in it]的返回值为零,并且您按照标准实践的建议检查它,然后过早返回(如果为零),那么基本上您的应用程序仍然无法正常工作。如果你想的话,即使那样如果(自己!=nil)检查是否存在,为了正确操作您的班级,99.99%的时间您确实需要自己非nil。现在,假设不管出于什么原因,[super init]返回了nil,基本上您对nil的检查基本上是将buck传递给您的类的调用方,在那里它可能无论如何都失败,因为它自然会假定调用是成功的。

基本上,我得到的是99.99%的时间,如果(自我!=nil)不会为您购买更强大的健壮性方面的任何东西,因为您只是将buck传递给您的调用程序。为了真正能够可靠地处理这个问题,实际上需要对整个调用层次结构进行检查。即便如此,它也会给你带来的唯一好处就是你的应用程序会更干净/更强大地失效。但它仍然会失败。

如果一个库类由于一个[super init]而擅自决定返回nil,那么无论如何,您都是被错误地对待了,这更说明库类的作者在实现上犯了错误。

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

但是对于C级代码,我仍然会针对空指针检查malloc()的返回值。然而,对于目标C,在我找到相反的证据之前,我认为我通常会跳过if(self!=零)检查。为什么存在差异?

因为,在C和malloc级别,在某些情况下实际上可以部分恢复。而我认为在Objective-C中,99.99%的情况下,如果[Super Init]返回零,那么即使你试图处理它,你也基本上是F**ed。你也可以让应用崩溃,然后处理后果。


这是对上述评论的总结。

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

如果你不遵守惯例

您的代码将在init方法的中间崩溃。(除非init没有任何意义)

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

您的代码很可能在以后的某个时候崩溃,因为您的实例是nil,在这里您期望的是不同的东西。或者你的程序会表现得出乎意料而不会崩溃。哦,天哪!你想要这个吗?我不知道…

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

您的代码文档!!)应该清楚地说:"退货……或者是nil",剩下的代码需要准备好处理这个问题。现在这是有道理的。


通常,如果类直接从NSObject派生,则不需要这样做。但是,有一个好习惯,就像你的类是从其他类派生的,它们的初始值设定项可能返回nil,如果是这样,你的初始值设定项就可以捕获它并正确地执行操作。

是的,作为记录,我遵循最佳实践并将其写在我所有的类上,甚至直接从NSObject派生的类上。


一个常见的错误是写作

1
self = [[super alloc] init];

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

1
self = [super init];

在设置子类成员之前,如果父类具有要首先初始化的成员(变量或其他对象),则需要使用。否则,objc运行时将它们全部初始化为0或nil。(与ANSIC不同,后者通常在不清除内存块的情况下分配内存块)

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


你是对的,你可以经常只写[super init],但这对任何子类都不起作用。人们宁愿只记住一行标准代码并一直使用它,即使有时只是必要的,这样我们就得到了标准的if (self = [super init]),它既考虑了返回nil的可能性,也考虑了返回self以外的对象的可能性。


这是为了检查inAZAZATION是否工作,如果init方法没有返回零,if语句返回true,因此它是检查对象创建正确的方法。我能想到init的一些原因可能会失败,可能是超级类不知道的一个init init方法或类似的东西,但我不认为这是常见的。但如果它真的发生了,那就更好了。


在OSX中,由于内存原因,-[NSObject init]失败的可能性不大。iOS也不能这么说。

此外,在对可能出于任何原因返回nil的类进行子类化时,编写该类也是一种良好的实践。