【问题标题】:Are you required to call the a super class's implementation from each and every subclass's function您是否需要从每个子类的函数中调用超类的实现
【发布时间】:2019-08-22 02:57:24
【问题描述】:

例如您定义了一个子类,它覆盖了其超类中的函数 f 。在override里面,是否需要调用超类的f函数。

【问题讨论】:

  • 这不是重复的。 OP 没有询问初始化程序。
  • ...而且 Swift 不是 Objective-C!
  • 没有。如果您必须调用方法的子类特定实现超类的通用实现,OOP 将根本不起作用。

标签: swift functional-programming subclass superclass


【解决方案1】:

答案是否定的,您通常不需要调用您在 Swift 或 OOP 中重写的超类方法。如果你这样做,那将是一个可怕的限制!有时您实际上不应该调用它,也许最常见的例子是当您以编程方式创建UIViewController 并且必须覆盖loadView() 而不调用super.loadView()

【讨论】:

  • super.loadView() 被调用以加载 IB 布局以及其中添加的内容
  • 他问你重写时是否需要调用超类方法。不,您不是必需的。每个以编程方式创建的视图控制器都会覆盖超类方法而不调用 super。
  • @bsod 大多数以编程方式创建的视图控制器在任何地方都调用super,因为它们覆盖viewDidLoad 而不是loadViewUIViewController.loadView 应该只在非常特殊的情况下被覆盖。
  • @Sulthan 以编程方式创建的视图控制器必须在 loadView 中创建自己的视图,并且绝不能调用 super.loadView。不存在不会在loadView 中创建其视图的编程创建视图控制器。
  • @bsod 所有控制器,无论是以编程方式创建还是加载,其行为都相同。当然,它们都使用loadView 来分配UIViewController.view,但是,只有在非常特殊的情况下,您才必须使用自定义实现来覆盖该方法。例如,UITableViewController 就是这样做的。我是说 99.9% 的控制器使用UIViewController.loadView 的默认实现。我认为在多年的 iOS 编程中,我从来不需要重写那个方法。
【解决方案2】:

这个问题的答案与 Swift 无关,但它与所有 OOP 语言有关。

简单的答案是:不,您不需要调用超类实现。

是否应该调用超类实现应该在文档中说明。当您不确定时,您通常应该始终调用超级实现。

你必须考虑超类在方法内部做了一些事情,如果你不调用超类实现,子类就不必正常工作。

不调用超类实现意味着您想要阻止超类实现发生,这通常意味着您的整个继承模型存在缺陷。

一个值得注意的例外是超类提供了一些您希望在子类中替换的默认实现(例如UIViewController.loadView)。不过,这不是一个非常常见的用例。

还有其他明显的例外,例如 getter。当您覆盖 getter 以返回自定义对象时,通常不需要调用 super。然而,再一次,即使是 getter 也可以在技术上初始化超类中的一些结构,并且可能需要调用 super。

【讨论】:

    【解决方案3】:

    默认情况下您调用super除非您知道您没有破坏该功能。


    我创建了一个gist。您可以将其复制到您自己的游乐场并玩弄它。但答案是:

    我对你有这种困惑。您基本上是在问扩展行为与覆盖行为有什么区别。

    Swift 不能很好地告诉你他们的不同之处。 他们的共同点是都需要用override 标记函数,但有时你在超类的实现之外做一些事情(扩展),有时你只是完全重写它(覆盖)

    假设我们有以下类:

    class Person {
    
        var age : Int?
    
        func incrementAge() {
            guard age != nil else {
                age = 1
                return
            }
            age! += 1
        }
    
        func eat() {
            print("eat popcorn")
        }
    }
    

    我们可以初始化它然后做:

    var p1 = Person()
    p1.incrementAge() // works fine
    

    现在假设我们这样做了:

    class Boy : Person{
    
        override func incrementAge() {
            age! += 2
        }
    }
    
    var b1 = Boy()
    b1.incrementAge()
    

    你认为会发生什么?!

    它会崩溃。因为在超类中,我们正在对age 进行nil 检查,但在我们的子类中我们从不调用super

    要使其正常工作,我们必须致电super

    class GoodBoy : Person{
    
        override func incrementAge() {
            super.incrementAge()
            age! += 2
        }
    }
    
    var b2 = GoodBoy()
    b2.incrementAge() // works fine. 
    

    我们可以不用直接打电话给super

    class AlternateGoodBoy : Person{
    
        override func incrementAge() {
            guard age != nil else {
                age = 1
                return
            }
            age! += 2
        }
    }
    
    var b3 = AlternateGoodBoy()
    b3.incrementAge() // works fine.
    

    ^^ 上述方法可行,但我们并不总是知道超类的实现。一个真实的例子是UIKit。我们不知道当viewDidLoad 被调用时会发生什么。因此我们必须调用super.viewDidLoad


    话虽如此,有时我们不能打电话给super 并且完全没问题,因为我们知道 super 做什么,或者可能只是不关心并想要完全摆脱它。例如:

    class Girl : Person{
        override func eat() {
            print("eat hotdog")
        }
    }
    
    var g1 = Girl()
    g1.eat() // doesn't crash, even though you overrode the behavior. It doesn't crash because the call to super ISN'T critical
    

    然而,最常见的情况是您调用super,但还要在其上添加一些内容。

    class Dad : Person {
        var moneyInBank = 0
        override func incrementAge() {
            super.incrementAge()
            addMoneyToRetirementFunds()
        }
    
        func addMoneyToRetirementFunds() {
            moneyInBank += 2000
        }
    }
    
    var d1 = Dad()
    d1.incrementAge()
    print(d1.moneyInBank) // 2000
    

    专业提示:

    与大多数先调用 super 然后调用其余部分的覆盖不同,对于 tearDown 函数,最好在函数末尾调用 super.tearDown()。通常,对于任何“删除”功能,您都希望在最后调用 super。例如viewWillDisAppear/viewDidDisappear

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多