29

我来自 C++ 世界,所以分配的概念this让我不寒而栗:

this = new Object; // Gah!

但在 Objective-C 中有一个类似的关键字 ,self这是完全可以接受的:

self = [super init]; // wait, what?

init许多示例 Objective-C 代码在例程中使用上述行。我的问题:

1)为什么分配self有意义(诸如“因为语言允许”之类的答案不算数)

2) 如果我不在self我的init例程中分配会发生什么?我是否将我的实例置于某种危险之中?

3)当以下if语句失败时,它是什么意思,我应该怎么做才能从中恢复:

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

    if (self)
    {
        self.my_foo = 42;
    }

    return self;
}
4

6 回答 6

34

这是一个经常被新手挑战的话题:

基本上,它源于这样一种想法,即超类可能已经覆盖了指定的初始化程序以返回与从返回的对象不同的对象+alloc。如果您没有将super' 初始化程序的返回值分配给self,那么您可能正在处理部分初始化的对象(因为super初始化的对象与您正在初始化的对象不同)。

总的来说,super返回不同的东西是非常罕见的,但它确实发生在一些情况下。

于 2009-08-27T15:23:44.443 回答
10

在 Objective-C 中,初始化程序可以选择在失败时返回 nil 或返回与调用初始化程序的对象完全不同的对象(例如,NSArray 总是这样做)。如果您不捕获 的返回值init,则该方法可能在已释放对象的上下文中执行。

如果您不希望从超类初始化程序中得到其他东西,那么有些人不同意您是否应该执行整个分配给自我的繁琐程序,但它通常被认为是良好的防御性编码。

是的,它看起来很奇怪。

于 2009-08-27T15:26:18.167 回答
6

确实nil,如果初始化失败,init 可能会返回。但这不是在实现自己的初始化程序时应该分配给 self 的主要原因。

前面已经提到过,但需要更加强调一下:初始化器返回的实例可能与您发送的实例不同,实际上它甚至可能不属于同一类!

一些类将此作为标准,例如所有初始化器都NSStringNSArray始终返回不同类的新实例。初始化器UIColor将经常返回专用类的不同实例。

如果你愿意,你自己可以很高兴地实现这样的东西:

-(id)initWithName:(NSString*)name;
{
  if ([name isEqualToString:@"Elvis"]) {
    [self release];
    self = [[TheKing alloc] init];
  } else if (self = [super init]){
    self.name = name;
  }
  return self;
}

这使您可以将某些特殊情况的实现分解为一个单独的类,而无需您的 API 的客户端关心甚至知道它。

于 2009-08-28T21:56:30.860 回答
3

这里的所有其他点都是有效的,但重要的是你要理解它self是每个 Objective-C 方法的隐式参数(objc_msgSend()传递它)并且可以写入,就像任何其他方法参数一样。(写入显式参数通常是不受欢迎的,除非它们是输出参数。)

通常,这仅在-init方法中完成,原因其他人已说明。它只是有任何影响,因为self从方法返回并在赋值中使用id obj = [[NSObject alloc] init];它也会影响 ivar 的隐式解析,因为例如,如果 myVar 是我的类的 ivar,那么在方法中访问它会导致它被隐式决心self->myVar

于 2009-08-27T15:52:33.263 回答
1

我还是 Objective C 的新手,但是这篇文章帮助我理解了这一点。

总而言之,大多数 init 调用返回 self 已经初始化的相同对象。如果有错误,那么 init 将返回 nil。此外,一些对象,如单例或唯一对象(如 NSNumber 0)将返回与初始化对象(单例或全局 0 对象)不同的对象。在这些情况下,您需要自引用该对象。我绝不是幕后发生的事情的专家,但从表面上看,这对我来说是有道理的。

于 2009-08-27T15:24:47.220 回答
1

如果[super init]返回 nil,则意味着您已被释放并且您的self参数现在是无效指针。通过盲目地遵循self = [super init]约定,您将避免潜在的令人讨厌的错误。

考虑以下非典型初始化程序:

- (id)initWithParam:(id)param {
    if (!param) {
        // Bad param.  Abort
        self = [super init]; // What if [super init] returns nil?
        [self release];
        return nil;
    }
    else 
    {
        // initialize with param.
        ...
    }
}

现在如果我的超类决定中止并返回 nil 会发生什么?我已被取消分配,我的self参数现在无效并且[self release]会崩溃。通过重新分配self,我避免了崩溃。

于 2009-08-28T00:02:09.843 回答