在Objective-C中自我赋值

29

我来自C ++的世界,因此给this分配值的概念让我感到不安:

this = new Object; // Gah!

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

self = [super init]; // wait, what?
很多Objective-C示例代码在init例程中使用以上行。我的问题是:
1)为什么将值赋给self是有意义的(像“因为语言允许”这样的答案不算)?
2)如果我在init例程中没有分配self,会发生什么?我是否会让我的实例处于某种危险之中?
3)当以下if语句失败时,它意味着什么,我应该怎么恢复:
- (id) init
{
    self = [super init];

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

    return self;
}

10
为什么人们在不同语言有不同习惯和规则时会感到惊讶?如果没有这些差异,所有语言就会变得相同。 - Terry Wilcox
1
在这种特定情况下,Objective-C 与 C 和 C++ 密切相关,而在这些语言中分配 this 的概念与分配 self 具有非常不同的含义。 - fbrereto
6
Objective-C与C密切相关,但与C++无关。它与C++完全无关,因此处理对象和对象初始化的约定也不同。 - Jason Coco
与你的问题无关,但是你的 init 方法应该返回 self... - Barry Wark
我通常使用 if ((self = [super init])) { ... }(双括号表示 '=' 故意不是 '==')。 - mk12
6个回答

35

这是一个新手经常面临的问题:

基本上,这源于一个概念:父类可能已经覆盖了指定的初始化方法来返回与+alloc返回的不同的对象。 如果您没有将super的初始化方法的返回值分配给self,那么您可能会处理部分初始化的对象(因为super初始化的对象和您正在初始化的对象不同)。

总体而言,super返回不同的情况相当罕见,但在某些案例中确实会发生此情况。


3
好的回答。另外,说到Objective-C,我总是相信 @bbum ... https://dev59.com/aXM_5IYBdhLWcg3wp06l - Quinn Taylor
2
你犯了一个错误的假设,认为网络上的所有内容都不会改变,这些链接将永远有用。但实际上,网络是会变化的,你提供的一半链接已经失效了。请在回答问题时,直接回答问题。引用和附上链接是可以的,但请确保真正地写出一个答案! - ArtOfWarfare

11

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

有些人 对于是否应该在没有从超类初始化器中获取到不同的返回值时进行 assign-to-self 操作存在异议,但通常被认为是良好的防御性编程。

没错,它看起来很奇怪。


6

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

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

有些类将此用作标准,例如所有初始化程序到 NSString NSArray 都将始终返回不同类的新实例。到 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的客户端关心或甚至知道它。


3
这里提到的其他观点都是正确的,但您也需要了解每个Objective-C方法都有一个隐式参数selfobjc_msgSend()会将其传递),可以像任何其他方法参数一样进行写入。 (通常不建议写入显式参数,除非它们是输出参数。)通常,这仅在-init方法中执行,出于其他人已经阐述的原因。它只有任何影响,因为self从该方法返回并在赋值id obj = [[NSObject alloc] init];中使用。它还会影响实例变量的隐式解析,例如,如果myVar是我的类的实例变量,则在方法中访问它会导致其被隐式解析为self->myVar

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,我避免了这个崩溃。


1

我还是Objective C的新手,但this post帮助我理解了这个问题。

总的来说,大多数init调用会返回与已初始化为self相同的对象。如果有错误,则init将返回nil。此外,一些对象(如单例或唯一对象(例如NSNumber 0))将返回与初始化的对象不同的对象(单例或全局0对象)。在这些情况下,您需要引用自身以指向该对象。我绝不是在幕后发生的事情方面的专家,但表面上对我来说很有意义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接