为什么默认调用超类指定的初始值设定项?

Why is the superclass designated initializer getting called by default?

我是 Swift 的新手,遇到了一些关于初始化程序的问题。我用以下代码创建了一个 Swift 文件:

import Foundation

class SuperClass
{
    var a : Int
    init()
    {
        a = 10
        print("In Superclass")
    }
}

class SubClass : SuperClass
{
    override init()
    {
        print("In Subclass")
    }
}

在上面的代码中,SubClassinit() 不包含对 SuperClassinit() 的调用,即 [=17] 中没有 super.init() =].

所以我的问题是:

1.为什么不调用指定的init() of SuperClass

为什么不报错

2. 如果我正在创建 SubClass 的对象,即 let s = SubClass(),输出为:

In Subclass

In Superclass

为什么调用 SuperClassinit()?默认情况下,子类 init() 是否调用超类 init()?

您没有访问 subclass 中的超级 class 变量,因此调用 super.init() 后跟 subclass 的初始化。但是,如果您尝试在 subclass 中使用 super class 变量而不调用它的初始化程序,那么它将导致编译时错误。

我自己没有尝试过,但是 Swift Language Guide 说:

Initializer Delegation for Class Types

To simplify the relationships between designated and convenience initializers, Swift applies the following three rules for delegation calls between initializers:

Rule 1

A designated initializer must call a designated initializer from its immediate superclass.

Rule 2

A convenience initializer must call another initializer from the same class.

Rule 3

A convenience initializer must ultimately call a designated initializer.

A simple way to remember this is:

Designated initializers must always delegate up.

Convenience initializers must always delegate across.

因此,由于调用 super.init() 是一个 'rule',如果未明确实现,它可能只是在内部完成。

每个class至少有一个指定初始化程序负责初始化实例变量。

这是文档的摘录:

Classes tend to have very few designated initializers, and it is quite common for a class to have only one. Designated initializers are “funnel” points through which initialization takes place, and through which the initialization process continues up the superclass chain.

Every class must have at least one designated initializer. In some cases, this requirement is satisfied by inheriting one or more designated initializers from a superclass, as described in Automatic Initializer Inheritance below.

您可以参考完整的文档了解更多详情:https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html

经验法则

  1. 为您的 class 创建一个指定的初始值设定项。
  2. 调用 superclass 的指定初始化程序或让系统为您解决这个问题。
  3. 创建零个或多个便利初始化器,它们将调用您指定的初始化器。

据我了解你的问题,你不仅想知道为什么、何时以及如何自动调用初始化程序,而且还抱怨缺少此行为的文档。

首先,我同意您关于缺乏文档的看法 - 就像您一样,我无法找到有关此行为的任何信息,因此 Apple 应该将其添加到文档中。

为什么叫super.init()

根据文档,超类的指定初始化程序必须由其子类的指定初始化程序调用,以便完全初始化所有属性。

Rule 1

A designated initializer must call a designated initializer from its immediate superclass.

上面的代码示例证明它显然是隐式完成的:print("In Superclass") 打印到控制台,因此 super.init() 在创建实例时以某种方式被调用。

何时以及如何调用 super.init()

要让编译器隐式调用超类的指定初始化程序,需要满足一些条件:

  1. 超类必须只有一个指定初始化器 然后被调用。否则编译器必须选择一个委托 到。这个单一的指定初始化器也可以是默认的 初始化程序或继承的初始化程序。

    class SuperClass {
        var a: Int
        init() {
            a = 10
        }
        // introduction of a second designated initializer in superclass:
        init(withValue value: Int) {
            a = value
        }
    }
    
    class SubClass: SuperClass {
        // won't compile:
        // "error: super.init isn't called on all paths before returning from initializer"            
        override init() {}
    }
    
  2. 超类的单个指定初始化器不能有任何 参数。毕竟编译器不知道任何合适的 要传递的参数。

    class SuperClass {
        var a: Int
        // declaration of an initializer with parameter:
        init(withValue value: Int) {
            a = value
        }
    }
    
    class SubClass: SuperClass {
        // won't compile:
        // "error: super.init isn't called on all paths before returning from initializer"            
        override init() {}
    }
    
  3. 子类的指定初始化器不能进一步阅读或 修改(继承)超类的实例属性或调用 超类的实例方法。那是因为 Swift 的 具有相应安全性的两阶段初始化过程 检查和事实,即隐式委托最多指定 超类的初始值设定项发生在 init-子类中的语句。

    Safety check 2

    A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.“

    Safety check 4

    An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

    class SuperClass {
        var a: Int
        init() {
            a = 10
        }
    }
    
    class SubClass: SuperClass {
        // won't compile:
        // "error: use of 'self' in property access 'a' before super.init initializes self"            
        override init() {
            a = 10 // modifying inherited self.a before phase 1 of initialization completes isn't valid! 
            // implicit delegation to super.init()
        }
    }
    

    Safety check 1

    A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.

    class SuperClass {
        var a: Int
        init() {
            a = 10
        }
    }
    
    class SubClass: SuperClass {
        // introduction of instance property "b"
        var b: Int
        // compiles finely:
        override init() {
            b = 10 // initializing self.b is required before delegation!
            // implicit delegation to super.init()
        }
    }
    

希望对您有所帮助。

Why the init() of SuperClass is getting called? Does a subclass init() calls the superclass init() by default?

基本上是的。

如果所有的规则都说你应该说super.init()而你不说,那就叫你了。

我不喜欢这种行为;它的记录很少,此外,偷偷为你做事似乎违背了 Swift 的精神。但我很久以前就针对它提交了一个错误,并被告知这是有意为之的行为。