我有一个关于在Objective-C中编写init方法的一般问题。
我到处都看到它(Apple的代码,书籍,开源代码等)init方法应该在继续初始化之前检查self = [super init]是否为nil。
init方法的默认Apple模板是:
- (id) init
{
self = [super init];
if (self != nil)
{
// your code here
}
return self;
}
为什么?
我的意思是什么时候才能返回nil?如果我在NSObject上调用init并且没有回来,那么一定要搞砸了,对吗?在这种情况下,你甚至可以不写程序......
类的init方法可能返回nil真的很常见吗?如果是这样,在什么情况下,为什么?
答案 0 :(得分:52)
例如:
[[NSData alloc] initWithContentsOfFile:@"this/path/doesn't/exist/"];
[[NSImage alloc] initWithContentsOfFile:@"unsupportedFormat.sjt"];
[NSImage imageNamed:@"AnImageThatIsntInTheImageCache"];
......等等。 (注意:如果文件不存在,NSData可能会抛出异常)。在发生问题时,有很多区域返回nil是预期的行为,并且由于这个原因,为了保持一致性,几乎一直都要检查nil是标准做法。
答案 1 :(得分:50)
这个特定的习语是标准的,因为它适用于所有情况。
虽然不常见,但仍会出现......
[super init];
...返回一个不同的实例,因此需要赋值给自己。
并且会出现返回nil的情况,因此需要进行nil检查,以便您的代码不会尝试初始化不再存在的实例变量槽。
最重要的是,它是使用的文档正确的模式,如果你不使用它,你做错了。
答案 2 :(得分:25)
我认为,在大多数课程中,如果[super init]的返回值为nil并按照标准惯例的建议进行检查,然后如果为nil则提前返回,基本上你的应用程序仍无法正常工作。 如果你考虑一下,即使if(self!= nil)检查在那里,为了你班级的正常运作,你实际上做的99.99%的时间需要自己是非零的。 现在,假设,无论出于何种原因,[super init] 确实返回nil,基本上你对nil的检查基本上是将你的责任传递给你的类的调用者,在那里它可能会失败,因为它将自然地假设呼叫成功。
基本上,我所得到的是99.99%的时间,if(self!= nil)并没有为你提供更强大的东西,因为你只是把责任推到你的调用者身上。要真正能够强有力地处理这个问题,您实际上需要在整个调用层次结构中进行检查。即便如此,它唯一能给你买的是你的应用程序会更加干净/健壮地失败。但它仍然会失败。
如果一个库类任意决定由于[super init]而返回nil,那么你几乎已经被删除了,这更多地表明了库类的作者犯了一个错误实施。
我认为当应用程序在更有限的内存中运行时,这更像是一种遗留的编码建议。
但是对于C级代码,我仍然通常会检查malloc()的返回值是否为NULL指针。然而,对于Objective-C,直到我找到相反的证据,我想我通常会跳过if(self!= nil)检查。为什么会出现差异?
因为,在C和malloc级别,在某些情况下,您实际上可以部分恢复。虽然我认为在Objective-C中,在99.99%的情况下,如果[super init]确实返回nil,那么即使你试图处理它,你也基本上都是f *** ed。您可能只是让应用程序崩溃并处理后果。
答案 3 :(得分:8)
这是上述评论的总结。
假设超类返回nil
。会发生什么事?
您的代码会在init
方法的中间崩溃。 (除非init
没有任何意义)
你的代码是probalby稍后会崩溃,因为你的实例是nil
,你期望有不同的东西。或者你的程序会出现意外行为而不会崩溃。噢亲爱的!你想要这个吗?我不知道......
您的代码文档(!)应明确说明:“return ...或nil”,其余代码需要为处理此代码做好准备。 现在它是有道理的。
答案 4 :(得分:7)
通常,如果您的班级直接来自NSObject
,则您不需要。但是,这是一个很好的习惯,就好像你的类来自其他类,它们的初始值设定项可能会返回nil
,如果是这样,你的初始化程序就可以捕获它并且行为正确。
是的,为了记录,我遵循最佳实践并将其写在我的所有课程上,甚至是那些直接来自NSObject
的课程。
答案 5 :(得分:3)
你是对的,你可以经常写[super init]
,但这对任何事物的子类都不起作用。人们更喜欢只记住一行标准代码并一直使用它,即使它只是有时是必要的,因此我们得到标准的if (self = [super init])
,它既可以返回nil,也可以使用nil。除了self
以外的对象被重新考虑。
答案 6 :(得分:3)
常见的错误是写
self = [[super alloc] init];
返回超类的实例,这不是你想要的子类构造函数/ init。 你得到一个不响应子类方法的对象,这可能会让人感到困惑,并且会产生令人困惑的错误,因为它们没有找到未找到的方法或标识符等等。
self = [super init];
如果超类在成立子类的成员之前有成员(变量或其他对象)初始化 ,则需要。否则,objc运行时将它们全部初始化为 0 或 nil 。 (与ANSI C不同,后者经常分配内存块而根本不清除它们)
是的,基类初始化可能会由于内存不足错误,组件丢失,资源获取失败等原因而失败,因此检查nil是明智的,并且需要不到几毫秒。
答案 7 :(得分:2)
这是为了检查intialazation是否有效,if语句如果init方法没有返回nil则返回true,因此它检查对象的创建是否正常工作。很少有理由我可以想到init可能会失败,也许它是一个超类初始化方法,超类不知道或类似的东西,我不认为这是常见的。但是,如果它确实发生了,那么最好不要发生碰撞,因为它总是被检查......
答案 8 :(得分:1)
在OS X中,由于内存原因-[NSObject init]
失败的可能性不大。对于iOS来说也是如此。
此外,在对可能因任何原因返回nil
的类进行子类化时,写入是一种很好的做法。