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

Jas*_*ien 163 null objective-c init

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

我看到它到处都是(Apple的代码,书籍,开源代码等)init方法应该在继续初始化之前检查self = [super init]是否为nil.

init方法的默认Apple模板是:

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

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

    return self;
}
Run Code Online (Sandbox Code Playgroud)

为什么?

我的意思是什么时候初始会返回零?如果我在NSObject上调用init并且没有回来,那么一定要搞砸了,对吗?在那种情况下,你甚至可以不写一个程序......

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

iKe*_*dac 52

例如:

[[NSData alloc] initWithContentsOfFile:@"this/path/doesn't/exist/"];
[[NSImage alloc] initWithContentsOfFile:@"unsupportedFormat.sjt"];
[NSImage imageNamed:@"AnImageThatIsntInTheImageCache"];
Run Code Online (Sandbox Code Playgroud)

... 等等.(注意:如果文件不存在,NSData可能会抛出异常).在发生问题时,有很多区域返回nil是预期的行为,并且由于这一点,为了一致性,标准做法几乎一直检查nil.

  • 是的,但这不是INSIDE各自的类'init方法.NSData继承自NSObject.NSData会检查[super init]是否返回nil?这就是我在这里问的问题.对不起,如果我不清楚... (10认同)
  • 如果你要对这些类进行子类化,那么[super init]将会返回nil.并非所有类都是NSObject的直接子类.从来没有任何保证它不会返回零.这只是一种通常鼓励的防御性编码实践. (9认同)
  • 我并不急于不遵循最佳做法.但是,我想知道它们为什么是最好的做法.就像被告知从塔上跳下来一样.除非你知道原因,否则你不要继续这样做.是否有一个大而软的枕头可以在底部降落,并获得巨额现金奖励?如果我知道我会跳.如果没有,我不会.我不想盲目跟随练习而不知道我为什么跟着它... (9认同)
  • 我怀疑NSObject init在任何情况下都可以返回nil.如果你的内存不足,那么alloc会失败,但如果成功,我怀疑init可能会失败 - NSObject甚至没有除Class之外的任何实例变量.在GNUStep中,它实现为"返回自我",而在Mac上的反汇编看起来是一样的.当然,这一切都是无关紧要的 - 只要遵循标准惯用语,你就不必担心它是否可以. (7认同)
  • 如果alloc返回nil,则init被发送到nil,这将总是导致nil,这最终将自己为nil. (4认同)
  • @Chuck,我没有得到的所有这些是,当你构造一个类的实例时,除非你因为某种原因知道它可能是null,否则你不会检查它是否为null.所以你的代码无论如何都会愉快地爆发.那么这些防弹构造函数在代码的上下文中是否有意义,而不检查对象是否为null?看起来像一个愚蠢的约定,简洁性确实很重要. (4认同)
  • @Jasarien:它可以在您调用的`super`的init方法中完成.当然,NSObject可能不会失败,但如果你是MyDataAndImageClass的子类,那么它的`init`很可能会失败. (2认同)

bbu*_*bum 50

这个特殊的习语是标准的,因为它适用于所有情况.

虽然不常见,但有些情况会......

[super init];
Run Code Online (Sandbox Code Playgroud)

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

并且会出现返回nil的情况,因此需要进行nil检查,以便您的代码不会尝试初始化不再存在的实例变量槽.

最重要的是它是使用的文档正确的模式,如果你不使用它,你做错了.

  • 根据可空性说明符,这仍然是正确的吗?如果我的超类的初始化程序是非null,那么还是值得检查更多的杂乱吗?(虽然NSObject本身似乎没有任何'-init` afaict ......) (3认同)

Gin*_*ino 26

我认为,在大多数课程中,如果[super init]的返回值为nil并按照标准实践的建议进行检查,然后如果为nil则提前返回,基本上你的应用程序仍无法正常工作.如果你想想看,即使是,如果(自我!=无)检查是存在的,为你的类的正确操作,你实际上是99.99%的时间需要自我是非零.现在,假设,无论出于何种原因,[super init] 确实返回nil,基本上你对nil的检查基本上是把你的责任推到了你的类的调用者,无论如何它可能会失败,因为它自然会假设调用是成功的.

基本上,我所得到的是99.99%的时间,if(self!= nil)并没有为你提供更强大的东西,因为你只是把责任推卸给你的调用者.要真正能够强有力地处理这个问题,您实际上需要在整个调用层次结构中进行检查.即便如此,它唯一能给你买的是你的应用程序会更加干净/健壮地失败.但它仍然会失败.

如果一个库类任意决定由于[超级初始化]而返回nil,那么你几乎都会被删除,这更多地表明库类的编写者犯了实现错误.

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

但对于C级代码,我通常仍然会检查malloc()的返回值是否对NULL指针.然而,对于Objective-C,直到我找到相反的证据,我想我通常会跳过if(self!= nil)检查.为什么会出现差异?

因为,在C和malloc级别,在某些情况下,您实际上可以部分恢复.虽然我认为在Objective-C中,在99.99%的情况下,如果[super init]确实返回nil,那么即使你试图处理它,你也基本上都是f***ed.您可能只是让应用程序崩溃并处理善后事宜.

  • 说得好.我同意那个. (6认同)
  • +1我完全同意.只是一个小小的注意事项:我不相信这种模式是由于分配更经常失败的结果.分配通常在调用init时完成.如果alloc失败,则甚至不会调用init. (3认同)

use*_*621 8

这是上述评论的总结.

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

如果您不遵守惯例

你的代码会在你的init方法中间崩溃.(除非init没有意义)

如果您遵循约定,不知道超类可能返回nil(大多数人最终在这里)

你的代码是probalby稍后会崩溃,因为你的实例是nil,你期望的东西不同.或者你的程序会出现意外行为而不会崩溃.噢亲爱的!你想要这个吗?我不知道...

如果您遵循约定,愿意允许您的子类返回nil

您的代码文档(!)应该明确说明:"return ...或nil",其余的代码需要准备好处理它.现在它是有道理的.

  • 我认为,这里有趣的一点是选项#1明显优于选项#2.如果在某种情况下您可能希望子类的init返回nil,则首选#3.如果发生的唯一原因是由于代码中的错误,那么使用#1.使用选项#2只是将您的应用程序推迟到稍后的某个时间段,从而在您更加努力地调试错误时完成工作.这就像默默地捕捉异常并继续而不处理它们. (5认同)

Joh*_*udy 7

通常,如果您的类直接来自NSObject,您将不需要.然而,这是一个习惯进入的好习惯,好像你的类派生自其他类,它们的初始化器可能会返回nil,如果是这样,你的初始化器就可以捕获它并且行为正确.

是的,为了记录,我遵循最佳实践并将其写在我的所有课程上,甚至是那些直接来自的课程NSObject.