为什么在Swift类中使用必需的初始值设定项?

为什么在Swift类中使用必需的初始值设定项?,swift,initialization,Swift,Initialization,我正在努力理解Swift类中required关键字的用法 class SomeClass { required init() { // initializer implementation goes here } } required不会强制我在子类中实现该方法。如果我想重写父类的required指定初始值设定项,我需要编写required,而不是override。我知道它是如何工作的,但不明白我为什么要这样做 必需的有什么好处? 据我所知,像C这样的语言并没

我正在努力理解Swift类中
required
关键字的用法

class SomeClass 
{
    required init() {
        // initializer implementation goes here
    }
}
required
不会强制我在子类中实现该方法。如果我想重写父类的
required
指定初始值设定项,我需要编写
required
,而不是
override
。我知道它是如何工作的,但不明白我为什么要这样做

必需的
有什么好处? 据我所知,像C这样的语言并没有这样的功能,而且可以很好地使用
override

,根据:

所以是的,required强制所有子类实现这个构造函数。然而,这是不必要的

 if you can satisfy the requirement with an inherited initializer.
因此,如果您创建了无法用父构造函数完全初始化的更复杂的类,则必须实现require构造函数

文档中的示例(添加了一些内容):


如果您试图在子类中添加您自己的初始化器,那么您必须遵循在超类中声明的某些内容。因此,它确保您不会忘记实现所需的方法。如果您忘记了编译器将给您错误
//致命错误,我们还没有包含必需的init()

。另一个原因是它创建了一组任何子类都应该遵循的条件。子类正在定义自己的初始值设定项。

这实际上只是满足编译器的一种方式,以确保如果这个类有任何子类,它们将继承或实现相同的初始值设定项。在这一点上存在疑问,因为如果一个子类有自己指定的初始值设定项,则不会继承来自超类的初始值设定项。因此,一个超类可能有一个初始值设定项,而子类可能没有<代码>必需克服了这种可能性

编译器需要以这种方式满足的一种情况涉及协议,其工作原理如下:

protocol Flier {
    init()
}
class Bird: Flier {
    init() {} // compile error
}
问题是,如果Bird有一个子类,那么该子类必须实现或继承
init
,而您并不能保证这一点。将Bird的
init
标记为
required
可以保证这一点

或者,您可以将Bird标记为
final
,从而保证反之,即它永远不会有子类

另一种情况是,您有一个工厂方法,可以通过调用相同的初始值设定项来生成类或其子类:

class Dog {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class NoisyDog: Dog {

}

func dogMakerAndNamer(whattype: Dog.Type) -> Dog {
    let d = whattype.init(name: "Fido") // compile error
    return d
}

dogMakerAndNamer
正在调用Dog或Dog子类上的
init(name:)
初始值设定项。但是编译器如何确保子类将具有
init(名称:)
初始值设定项?
required
名称平息了编译器的恐惧。

我想提请大家注意
required
提供的另一个解决方案,除了上面Matt给出的解决方案之外

class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"

}
let instanceSubClass = subClass()
instanceSubClass.name        //output: "Untitled"
instanceSubClass.neakName    //output: "Subclass Untitled"
正如您在上面的示例中所看到的,我已经在
超类上声明了
required init()
init()
超类的初始值设定项默认继承了
子类上的
子类,因此您可以创建子类
let instanceSubClass=subClass()
的实例

但是,假设您想在子类上添加一个指定的初始值设定项,以将运行时值分配给存储的属性
neakName
。当然,您可以添加它,但这将导致超类中的初始值设定项不会继承到子类,因此,如果您要创建
子类的实例,您将通过其自己指定的初始值设定项创建,如下所示

class superClass{
    var name: String
    init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"
class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}    // Compiler error <------------ required `init()` must be provided by subClass.
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"  
在上面的这里,您不能仅仅通过
子类()
创建
子类的实例,但是如果您想让超类的每个子类都有自己的
init()
初始值设定项来创建
子类()
的直接实例。只需将
required
关键字放在超类上的
init()
之前,它将强制您在
子类上添加
init()
初始值设定项,如下所示

class superClass{
    var name: String
    init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"
class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}    // Compiler error <------------ required `init()` must be provided by subClass.
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"  
类超类{
变量名称:String
必需的init(){
//初始化器实现在这里
self.name=“无标题”
}
}
类子类:超类{
var neaname:String=“子类未命名”
init(neakame:String){
self.neakame=neakame
}

}//由于继承的原因,不需要在子类中实现编译器错误,这很有意义。然而,我也很想知道这里的好处。如果一个子类(假设覆盖
init()
,但不是某个必需的初始值设定项)通过调用继承的必需初始值设定项的进程获得init,并且该必需的初始值设定项随后调用
init()
,那么它不会调用该子类的“覆盖的init”,一切正常吗?--我认为唯一好的理由是,所需的初始值设定项不在self上调用
init()
,因此将永远不会调用重写“init()”。这在上下文中可能很有趣:。这没有意义。然后,为了解决这个问题,您需要在子类上提供init(),这样子类var就没有设置。这导致了大量无法维护的代码,依赖于var。。。!和运行时错误,如果曾经使用过“required”init而没有返回并清理这些“将在使用”vars之前初始化”,而不是让。。。要求init()调用super.init(withanyargs)可以更好地解决这个问题。所以,如果“noisedog”有“let noise:String”,那么在“required init(name:String)”中您会怎么做?是将var更改为“var noise:String!”的“正确”方法希望如果有人开始使用Dog.Type,他们也知道如何设置“noise”var之后?或者您是否必须提供一些自定义初始值设定项替换的默认值,如“var noise=“Arf!”?我在这里寻找最佳实践(这似乎是一个考虑不周的语言结构,b