【问题标题】:Why do we use [super dealloc] and not [self dealloc] or [object dealloc]为什么我们使用 [super dealloc] 而不是 [self dealloc] 或 [object dealloc]
【发布时间】:2026-01-06 16:50:01
【问题描述】:

演讲是关于 Objective-c 中的类和对象。 我无法理解 [super dealloc] 的概念。 我们有一些类说 myClass,它继承自 NSObject。它有一些方法并从父类继承其他方法。所以我们这里也有 dealloc 方法。为什么我不能只使用 [myInstance dealloc]? 我认为我们调用父类方法来杀死类的实例是否正确?为什么这么复杂?

这不是关于 [myInstance release] 的讨论,我同意这个概念。

【问题讨论】:

    标签: objective-c ios cocoa-touch cocoa


    【解决方案1】:

    -dealloc 被调用时,约定是它在类层次结构中向上工作,以确保正确处理每个父类使用的内存。 [self dealloc] 会再次调用你的方法并给你一个无限循环。 [super dealloc] 拆除你的超类设置——如果那个超类是NSObject,它也会释放对象占用的内存。

    【讨论】:

    • 为什么是无限循环?我不太明白。让我们来看看。我的类有一些方法,假设它有几个实例变量和一些来自 NSObject 的方法。我从它本身调用 dealloc 。它释放变量和方法占用的内存。怎么会进入死循环?一旦记忆被清除,它就会停止?
    • 永远打电话给[self dealloc]曾经。您只需在 -dealloc 实现结束时调用 [super dealloc]。但是想象一下,如果你在那里打电话给[self dealloc] 会发生什么。你会调用你自己的代码,它会调用你自己的代码,它会调用你自己的代码,它会调用你自己的代码,等等。
    • 除了从您自己的dealoc 实现中调用super 之外,您永远不会调用任何dealloc
    • @Dvole 因为这就是 [self «methodName»] 在所有 Objective-C 中总是做的——它在当前实例上调用 «methodName»,使用实例的(子大多数)类的方法 (除非该子类没有实现该方法,在这种情况下,它会一直在超类链中查找该名称的方法)。因此,如果您有一个具有- methodA 方法和- methodB 的类,并且在- methodA 内部调用[self methodB],您将能够看到程序流向- methodA,然后是- methodB,然后返回结束- methodA
    • @Dvole 但是如果- methodA 调用[self methodA],那么你会看到程序流入- methodA,然后再次递归到- methodA (现在是- methodA在程序堆栈上两次),然后一次又一次,直到程序堆栈耗尽深度并且堆栈溢出。这在 C++ 或 C# 或 Java 或 Ruby 或任何其他 OO 语言中的工作方式相同 - 一种方法无条件地调用自身并带有堆栈溢出。 Objective-C 中的[super methodA] 只是说“在我自己的这个实例上调用methodA,但是寻找从超类开始的方法,而不是这个类”
    【解决方案2】:

    不,你是对的。

    父类(在您的示例中为 NSObject)必须运行自己的指令。 这就是为什么在您编写发布说明之后,您将dealloc 发送给超类以完成该过程。

    【讨论】:

      【解决方案3】:

      简而言之,它是初始化程序的补充。这个概念是初始化从继承层次结构的顶部传播到底部以首先初始化所有父字段,并且释放从底部传播到顶部以首先清理所有子字段。在您的init 方法中,您总是首先调用[super init],在dealloc 中,您总是最后调用[super dealloc]。有关[super dealloc] 的更多信息,请查看this question

      【讨论】:

        【解决方案4】:

        你这样做:

        @interface MyClass : SuperClass
        @end
        
        @implementation MyClass
        
        - (void)dealloc
        {
            // Release stuff allocated in MyClass
            [super dealloc];
        }
        
        @end
        

        那是什么意思呢?假设你有一个MyClass 的实例,并且它已经被释放/自动释放,直到它的保留计数下降到 0。Objective-C 现在想要销毁该实例。为了清理东西,Objective-C 调用 dealloc 方法。

        现在,调用的是 MyClass 中定义的 dealloc。但是您已经从其他类派生了该类。这意味着这个超类也可能有一些东西需要清理。所以你必须调用[super dealloc];,这意味着:执行 SuperClass 中定义的dealloc

        类似的概念适用于您的initinitFoo 方法:在其中您执行self = [super init]; 之类的操作,因此超类可以进行其初始化。

        另外,你不应该直接打电话给dealloc!它意味着只有在需要清理对象时才由 Objective-C 调用。

        顺便说一句,self 表示:我的对象的当前实例,而super 表示:我的对象的当前实例,但使用超类定义的方法。

        【讨论】:

        • 是的,那部分很清楚,但是为什么要打扰超类呢?我认为当我从 NSObject 继承 myClass 时,它会将其方法和变量硬复制到 myClass 中,以便于使用。但不是吗?所以每次我调用父方法时,它都会一直查找到 NSObject?如果方法只是从超类中复制出来,不是更容易吗?而不是谈论更多的开销和资源。
        • @Dvole:你得到了变量,但你必须初始化并清理它们。例如,假设您从UIView 派生:您不知道它的变量,即使您知道它们,您也不知道如何 初始化它们以及如何清理它们。这就是为什么必须使用[super initWithFrame:aFrame][super dealloc]
        【解决方案5】:

        -dealloc 中使用[self dealloc] 会导致无限递归。 -dealloc 方法只会继续调用自己。你不能在-dealloc 中使用[object dealloc],因为除了self 之外,你没有也不应该有指向自己的指针。你是对的,NSObject 需要自己进行清理。一个类创建的所有东西都负责清理。此外,[super dealloc] 必须是-dealloc 的最后一行。否则,在那之后您可能没有分配的对象。

        【讨论】:

          【解决方案6】:

          您的问题实际上是一个更通用的问题,可以从dealloc 问题中删除。

          重写方法以及何时调用super

          有时,当您进行子类化时,您会覆盖现有方法。有时你想利用那个方便的时间来执行一些行为,有时你想阻止父母做某事并改变行为。

          - (void)methodOne {
            // don't let silly parent class do method one stuff
          
            // do my stuff …
          }
          
          - (void)methodTwo {
            // let parent perform his behavior first
            [super methodTwo];
          
            // do my stuff …
          }
          
          - (void)methodThree {
            // do my stuff …
          
            // let parent do his stuff after I have
            [super methodThree];
          }
          
          - (void)methodFour {
            // it's not common, but you might want to split your stuff with a call to
            // super - this usually involves knowledge of what super is up to
          
            // do some stuff …
            [super methodFour];
            // do my stuff …
          }
          

          许多方法/类的文档(请参阅 UIView 和 NSManagedObject)可能会说明您是否可以或应该或不应该覆盖方法。好的文档会告诉你什么时候应该调用 super。

          在调用[super dealloc] 时,您应该在释放您持有的资源后最后调用它。 (因为您可能引用的其他内存可能会被您的父类释放)。

          super 的另一个经常调用是在 init 方法中。如果你实现了一个 init 方法,你应该重写父级的“指定初始化器”,你应该从你的调用 super。

          @implementation CustomViewController
          
          - (id)init {
            return [super initWithNibName:@"CustomView" bundle:nil];
          }
          
          - (id)initWithNibName:(NSString *)name bundle:(NSBundle *)bundle {
            return [self init];
          }
          
          //…
          

          这种常见模式可防止任何人使用不正确的 nib 文件加载您的类。 (这可能需要也可能不需要,这是另一个问题。)

          如果碰巧有其他初始化器,根据定义,它们应该调用它们指定的初始化器。因此,如果 UIView 要添加 initWithNibName: 方法,它可能会调用 [self initWithNibName:name bundle:nil],然后将其“捕获”并重定向到您的预期初始化程序。

          【讨论】:

            【解决方案7】:

            已经有一些很好的答案,但我会更直接地回答问题:

            1. 我们为什么写[super dealloc]

              我们编写它是因为这实际上会破坏实例。否则内存永远不会被释放。

            2. 我们为什么不写[self dealloc]

              我们不能这样写,因为您应该明确调用dealloc 的唯一地方是在dealloc 方法内。在dealloc 中执行[self dealloc] 只会使方法调用自身,然后调用自身,调用自身,调用自身,调用自身……。

            3. 我们为什么不写[myInstance dealloc]

              我假设myInstance 只是另一个指向对象的方法中变量的占位符,而您问我们为什么不只是通过该变量调用dealloc。这是因为你不知道什么时候应该释放对象retainrelease 的全部意义在于让您不必跟踪对象何时准备好被销毁。您只需正确地保留和释放对象,当release 已被调用足够多次时,表明该对象没有更多的所有者想要保留对它的引用,它会自行调用dealloc

            【讨论】:

              最近更新 更多