在Objective-C中,为什么我应该检查self = 是不是nil?
我有一个关于在Objective-C中编写init方法的一般问题。
我发现无处不在(苹果的代码,书籍,开源代码等等)init方法应该在继续初始化之前检查self = [super init]是否为零。
init方法的默认Apple模板是:
- (id) init { self = [super init]; if (self != nil) { // your code here } return self; }
为什么?
我的意思是什么时候init会返回零? 如果我在NSObject上调用init并得到零,那么一定是真的搞砸了吧? 在这种情况下,你甚至可以不写一个程序…
一个类的init方法可能返回nil是否真的很普遍? 如果是这样,在什么情况下,为什么?
例如:
[[NSData alloc] initWithContentsOfFile:@"this/path/doesn't/exist/"]; [[NSImage alloc] initWithContentsOfFile:@"unsupportedFormat.sjt"]; [NSImage imageNamed:@"AnImageThatIsntInTheImageCache"];
… 等等。 (注意:如果文件不存在,NSData可能会引发exception)。 在出现问题时,有很多地方返回nil是预期的行为,并且正因为如此,为了一致性的缘故,几乎所有的时间都检查nil。
这个特殊的习惯用法是标准的,因为它适用于所有情况。
虽然不常见,但有些情况下…
[super init];
…返回一个不同的实例,因此需要赋值给自己。
而且会有返回nil的情况,因此需要使用nil检查,以便代码不会尝试初始化不再存在的实例variables插槽。
底线是这是使用文件正确的模式,如果你不使用它,你做错了。
我认为,在大多数类中,如果[super init]的返回值是零,并且按照标准惯例的build议检查它,然后提前返回(如果为零),基本上,您的应用程序仍然不能正常工作。 如果你仔细想想,即使你有(自我!=无)检查,为了你的class级的正常运行,你实际上需要自我的99.99%是非零的。 现在,无论出于何种原因,假设[super init] 没有返回零,基本上你的检查是无效的,基本上是把它传递给你的类的调用者,在那里它可能会失败,因为它自然会认为调用是成功的。
基本上,我所得到的是99.99%的时间,if(self!= nil)不会为了更强大的稳健性而向你提供任何东西,因为你只是把钱花在了调用者身上。 要真正能够强有力地处理这个问题,实际上需要在整个调用层次结构中进行检查。 即使如此,它会买你的唯一的事情是,你的应用程序会更干净/强大一点。 但它仍然会失败。
如果一个图书馆类由于[超级初始化]而任意决定返回零,那么反正你会觉得非常失败,这更像是图书馆类的作者犯了一个错误的实现。
我认为这是一个传统的编码build议,当应用程序在更有限的内存中运行。
但是对于C级代码,我仍然会检查malloc()的返回值是否为NULL指针。 而对于Objective-C,直到我find相反的证据,我想我通常会跳过if(self!= nil)检查。 为什么这个差距?
因为在C和malloc级别,在某些情况下,你实际上可以部分恢复。 而我认为在Objective-C中,在99.99%的情况下,如果[super init]返回nil,那么即使您尝试处理它,也基本上是被修改的。 你可能只是让应用程序崩溃,处理善后事宜。
这是上面评论的总结。
假设超类返回nil
。 会发生什么?
如果你不遵守这些惯例
你的代码会在init
方法的中间崩溃。 (除非init
没有任何意义)
如果你按照惯例,不知道超类可能会返回零(大多数人最终在这里)
你的代码会在稍后的某个时候崩溃,因为你的实例是nil
,你期望有什么不同的地方。 或者你的程序会出乎意料地不会崩溃。 噢亲爱的! 你想要这个吗? 我不知道…
如果你遵循约定,愿意让你的子类返回零
您的代码文档(!)应该清楚地声明:“返回…或无”,其余的代码需要准备处理这个。 现在它是有道理的。
通常,如果你的类直接从NSObject
派生,你将不需要。 然而,这是一个很好的习惯,好像你的类是从其他类派生的,它们的初始值设定项可能会返回nil
,如果是的话,那么初始值设定项就可以捕获它并正确地运行。
是的,为了logging,我遵循最佳实践,并将其写在我的所有课程上,即使是直接从NSObject
派生的课程。
你是对的,你通常可以写[super init]
,但是这对任何东西的子类都不起作用。 人们更喜欢记住一个标准的代码行,并且一直使用它,即使它只是有时是必要的,因此我们得到了标准的if (self = [super init])
,它既可能返回零,一个对象以外的对象的可能性被返回。
一个常见的错误是写
self = [[super alloc] init];
它返回超类的一个实例,这不是你想要在子类的构造函数/ init中。 你得到一个对子类方法没有响应的对象,这会造成混淆,并且产生关于不响应方法或未find的标识符的混淆错误等。
self = [super init];
如果超类有成员(variables或其他对象)初始化设置子类的成员之前需要。 否则,objc运行时将它们全部初始化为0或零 。 ( 与ANSI C不同,它经常分配内存块而不清除它们 )
是的,由于内存不足错误,组件丢失,资源获取失败等原因,基类初始化可能会失败,因此检查零是明智的,并且需要不到几毫秒的时间。
这是为了检查是否工作,如果init方法没有返回nil,那么if语句返回true,所以它检查对象的创build是否正常工作。 几乎没有什么理由可以想到init可能会失败,也许它是一个超类不知道或者类似的东西,我不认为这是常见的。 但是,如果真的发生了,最好不要发生这样的事情,因为它总是检查…
在OS X中,由于内存原因, -[NSObject init]
不太可能失败。 iOS也不能这么说。
另外,对于无论什么原因返回nil
的类进行子类化时,编写这个程序都是很好的做法。