【问题标题】:Why should I call self=[super init]为什么我要调用 self=[super init]
【发布时间】:2010-06-02 10:49:58
【问题描述】:

假设我创建了我的类及其init 方法。为什么我应该调用并返回分配给自我的超类init 的值?涵盖哪些情况?

我会很感激示例为什么我需要它来用于 Cocoa 超类和非 Cocoa。

【问题讨论】:

    标签: objective-c


    【解决方案1】:

    你的意思是为什么

    self = [super init];
    

    而不是

    [super init];
    

    两个原因:

    1. 在初始化中,失败由返回 nil 指示。您需要知道超级对象的初始化是否失败。
    2. 超类可能会选择将 +alloc 返回的自身替换为不同的对象。这种情况很少见,但使用class clusters 最常发生。

    针对迈克尔的评论进行了编辑:

    我可以理解为什么我需要保存并返回 [super init]。但这只是惯例和好看让我们使用 self 作为临时变量来传递结果吗?

    没有。实例变量是相对于自身指针访问的,所以如下:

    -(id) init
    {
        self = [super init];
        if (self != nil)
        {
            myBoolIvar = YES; 
           // The above is an implicit version of self->myBoolIvar = YES;
        }
        return self;
    }
    

    self 显然必须指向正确的内存块,即您要返回的内存块。

    另一点是,如果 super init 返回不同的类实例,那么该行之后的其余代码甚至可能没有意义,导致内存泄漏和崩溃,甚至不谈论从该类实例化的对象。

    这可能是个问题。如果我将 NSNumber 子类化并且 [super init] 决定返回一个 NSString (它可以 - 没有什么可以阻止它),那显然是一场灾难。从 -init 返回的任何 super 都必须与子类“兼容”,即为 ivars 提供空间并进一步子类化,否则这是一个可怕的错误(当然,除非问题已记录在案)。所以,一般来说,你不需要担心检查类。但是,请阅读文档。例如,请参阅 NSString 的文档中关于子类化 NSString 的部分。

    【讨论】:

    • 很好地抓住了被忽视的原因二。不错的答案!
    • 所以我想澄清两点。要将init 结果从顶部冒泡到我的班级和我的子班级,我可以理解为什么我需要保存并返回[super init]。但这只是惯例和好看让我们使用 self 作为临时变量来传递结果吗?另一点是,如果super init 返回不同的类实例,那么该行之后的其余代码甚至可能没有意义,导致内存泄漏和崩溃,甚至不谈论从该类实例化的对象。所以我可能还需要检查super init返回的类。
    • 确实感谢您的更新。这解释了我,也让我更清楚了。需要再问几个问题才能在我的脑海中画出全貌,这将在单独的问题中进行。
    【解决方案2】:

    我知道我的回答有点晚了,但我无法阻止自己发布一个链接,我发现该链接对消除我对这个问题的疑虑非常有用。

    Matt Gallagher: What does it mean when you assign [super init] to self?

    编辑:根据 cmets,这里是链接中的要点

    要理解为什么self=[super init]; 我们需要考虑很多点。让我们一一解决。

    什么是self

    每个方法都有两个隐藏参数:self_cmd。所以方法调用

    - (id)initWithString:(NSString *)aString
    

    被编译器改成这样的函数调用

    id initWithString(id self, SEL _cmd, NSString *aString);
    

    我们为什么需要自我?

    实际情况是编译器使用self 参数来解析对方法内实例变量的任何引用。

    假设我们有一个方法setValueToZero,而value是它所属类的实例变量,那么实现

    - (void)setValueToZero
    {
        value = 0;
    }
    

    会被编译器转换成这样的函数

    void setValueToZero(id self, SEL _cmd)
    {
        self->value = 0;
    }
    

    self 在调用init 时是否已经有值?

    以下是一个典型的对象创建和初始化示例。

    [[MyClass alloc] initWithString:@"someString"]
    

    在这里,当我们进入initWithString 方法时,self 将拥有新分配的对象作为它的值(即来自[MyClass alloc] 的返回值)。事实上,它几乎可以保证是正确的最终值。

    为什么是self = [super init];

    这是因为[super init] 被允许做以下三件事之一:

    1. 返回它自己的接收器(self 指针不会改变)并初始化继承的实例值。
    2. 返回一个不同的对象并初始化继承的实例值。
    3. 返回nil,表示失败。

    在第一种情况下,赋值对self 没有影响。第三种情况,初始化失败,self设置为nil,返回。

    分配给self 的原因是第二种情况。考虑以下

    - (id)initWithString:(NSString *)aString
    {
        self = [super init];
        if (self)
        {
            instanceString = [aString retain];
        }
        return self;
    }
    

    我们希望从

    instanceString = [aString retain];
    

    self->instanceString = [aString retain];
    

    要作用于正确的值,因此我们必须更改 self 的值。

    [super init] 何时会返回不同的对象?

    以下情况之一

    1. 单例对象(总是返回单例而不是任何后续分配)
    2. 其他唯一对象([NSNumber numberWithInteger:0] 始终返回全局“零”对象)
    3. 当您初始化超类的实例时,类簇会替换私有子类。
    4. 根据传递给初始化程序的参数选择重新分配相同(或兼容)类的类。

    除最后一种情况外,如果返回的对象发生更改,则继续初始化它是错误的——返回的对象已经完全初始化,不再需要与您的类相关。所以更好的初始化方法如下

    - (id)initWithString:(NSString *)aString
    {
        id result = [super init];
        if (self == result)
        {
            instanceString = [aString retain];
        }
        return result;
    }
    

    结论

    您无需将[super init] 分配给self 即可使大多数课程正常工作。在一些不起眼的情况下,这实际上是错误的做法。

    那我们为什么还要继续分配给self呢?它是初始化器的传统模板,虽然在某些情况下它是错误的,但在其他情况下它是正确的,因为它是为期待这种方法而编写的。

    【讨论】:

    • 根据乔希的评论,请您在回答中包含链接文章的基本部分,并提供link for reference。不这样做会使答案面临链接腐烂和删除的风险。
    • 很好的答案,除了更好的初始化方法。那是完全错误的;假设将初始化不同的对象是不安全的。看NSStringinit 方法几乎总是返回不同的实例,但它们不是预先存在的实例。如果您希望强制执行单例,那么必须在调用init 之前完成,并且*必须在初始化流程之外您的子类初始化程序必须知道采用的单例模式。
    【解决方案3】:

    基本上每个 Objective-C 类都是一个子类。它要么是您指定的某个类,要么是 NSObject。

    在您调用的子类(您正在处理的类)中 self = [超级初始化]

    这基本上是调用超类(我上面提到的那些)的init方法(构造函数)并将其分配给当前类。

    这确保调用超类的初始化方法。

    现在 如果(自己) 这基本上检查了上面的代码是否有效。

    这样做是为了确保如果您调用超类的某个实例变量,您将能够这样做。

    【讨论】:

      【解决方案4】:

      在大多数情况下,将 self 设置为 [super init] 没有任何作用,因为 [super init] 无论如何都会返回 self。然而,在极少数情况下,[super init] 会返回不同的东西。如果由于某种原因未能初始化超类,它可能会返回 nil,或者它可能决定返回完全不同的对象。

      【讨论】:

        【解决方案5】:

        来源


        Implementing The Designated Initializer

        **

        自我

        ** 在方法内部,self 是一个隐式局部变量。不需要声明它,它会自动设置为指向发送消息的对象。 (在 Android 编程中类似这样。)通常,使用 self 对象可以向自己发送消息。此处示例:

         return self;  
        

        **

        超级

        ** 当我们重写一个方法时,我们希望保留超类的方法正在做什么,并让您的子类在其上添加一些新的东西。为了方便起见,Objective-C 中有一个编译器指令,称为 super。 super 是如何工作的? 通常,当您向对象发送消息时,会在对象的类中开始搜索具有该名称的方法。如果没有这样的方法,则在对象的超类中继续搜索。搜索将继续沿继承层次结构向上,直到找到合适的方法。 (如果它到达层次结构的顶部并且没有找到方法,则会引发异常)。 当我们向 super 发送消息时,我们正在向 self 发送消息,但是搜索方法会跳过对象的类并从超类开始。在上述情况下,我们向 super 发送初始化消息。这会调用 NSObject 的 init 方法。

        【讨论】:

          【解决方案6】:

          因为您必须以某种方式初始化对象,并且大概您想要执行任何需要完成超类的初始化,因为您是从它下降的。

          【讨论】:

          • 那我为什么不能直接调用[super init] 而不将结果返回为self?据我了解,self 无论如何都是在本地修改的。
          • 那么 self 是什么?你在说“我现在是 [super init] 返回的对象。”
          • 这样return [super init] 无效?如果我修改 self 它只会在 init 方法的范围内
          • 如果你 return [super init] 你可能根本不用 init 方法 - 那么 [super init] 将被直接调用。
          猜你喜欢
          • 1970-01-01
          • 2011-03-04
          • 2010-11-20
          • 2012-11-22
          • 1970-01-01
          • 2012-03-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多