为什么Swift中甚至需要便利关键字?

为什么Swift中甚至需要便利关键字?,swift,initialization,Swift,Initialization,由于Swift支持方法和初始值设定项重载,您可以将多个init放在一起,并使用您认为方便的选项: class Person { var name:String init(name: String) { self.name = name } init() { self.name = "John" } } 那么,便利性关键字为什么会存在呢?是什么使以下各项实质上更好 class Person { var name:

由于Swift支持方法和初始值设定项重载,您可以将多个
init
放在一起,并使用您认为方便的选项:

class Person {
    var name:String

    init(name: String) {
        self.name = name
    }

    init() {
        self.name = "John"
    }
}
那么,
便利性
关键字为什么会存在呢?是什么使以下各项实质上更好

class Person {
    var name:String

    init(name: String) {
        self.name = name
    }

    convenience init() {
        self.init(name: "John")
    }
}

首先,我想到的是,它被用于类继承中,用于代码组织和可读性。继续你的
Person
课程,想象这样一个场景

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

    convenience init(){
        self.init(name: "Unknown")
    }
}


class Employee: Person{
    var salary: Double
    init(name:String, salary:Double){
        self.salary = salary
        super.init(name: name)
    }

    override convenience init(name: String) {
        self.init(name:name, salary: 0)
    }
}

let employee1 = Employee() // {{name "Unknown"} salary 0}
let john = Employee(name: "John") // {{name "John"} salary 0}
let jane = Employee(name: "Jane", salary: 700) // {{name "Jane"} salary 700}
convenience init(maleWithName: String) {
   self.init(name: name)
   gender = .Male
}

convenience init(femaleWithName: String) {
   self.init(name: name)
   gender = .Female
}

使用便利性初始值设定项,我可以创建一个没有值的
Employee()
对象,因此单词
便利性

非常清晰。从你的第二个例子来看

init(name: String) {
    self.name = name
}
是必需的或指定的。它必须初始化所有常量和变量。方便的初始值设定项是可选的,通常可用于简化初始化。例如,假设您的Person类有一个可选变量性别:

var gender: Gender?
其中性别是一个枚举

enum Gender {
  case Male, Female
}
您可以使用这样的方便初始值设定项

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

    convenience init(){
        self.init(name: "Unknown")
    }
}


class Employee: Person{
    var salary: Double
    init(name:String, salary:Double){
        self.salary = salary
        super.init(name: name)
    }

    override convenience init(name: String) {
        self.init(name:name, salary: 0)
    }
}

let employee1 = Employee() // {{name "Unknown"} salary 0}
let john = Employee(name: "John") // {{name "John"} salary 0}
let jane = Employee(name: "Jane", salary: 700) // {{name "Jane"} salary 700}
convenience init(maleWithName: String) {
   self.init(name: name)
   gender = .Male
}

convenience init(femaleWithName: String) {
   self.init(name: name)
   gender = .Female
}

便利初始值设定者必须调用其中指定或要求的初始值设定者。如果您的类是一个子类,那么它必须在初始化过程中调用
super.init()

除了其他用户在这里解释的要点之外,我还有一点理解

我强烈地感觉到便利初始值设定项和扩展之间的联系。对于我来说,当我想要修改(在大多数情况下使其简短或简单)现有类的初始化时,方便的初始值设定项是最有用的

例如,您使用的某个第三方类具有四个参数
init
,但在您的应用程序中,后两个参数的值相同。为了避免更多的输入并使代码更干净,您可以定义一个仅使用两个参数的
便利初始化
,并在其内部调用带有默认值的last To参数的
self.init

根据
便利
初始化者必须遵守一些特定规则:

  • 便利性
    初始值设定项只能在同一时间调用初始化器 类,不在超级类中(仅跨类,不向上)

  • 便利性
    初始值设定项必须调用指定的初始值设定项 链条中的某个地方

  • 便利性
    初始值设定项不能更改其前面的任何属性 调用了另一个初始值设定项-而指定的初始值设定项 必须初始化当前类引入的属性 在调用另一个初始值设定项之前


  • 通过使用
    便利性
    关键字,Swift编译器知道它必须检查这些条件,否则它不能。现有答案只讲述了
    便利性
    故事的一半。故事的另一半,即现有答案都没有涵盖的那一半,回答了德斯蒙德在评论中提出的问题:


    为什么斯威夫特会强迫我把
    便利性
    放在我的初始值设定项前面,仅仅因为我需要从它调用
    self.init
    `

    我在中略微提到了它,在中我详细介绍了Swift的几个初始值设定规则,但主要关注的是
    必需的
    单词。但这个答案仍然在解决一些与这个问题和这个答案相关的问题。我们必须了解Swift初始值设定项继承是如何工作的

    因为Swift不允许未初始化的变量,所以不能保证从继承的类继承所有(或任何)初始值设定项。如果我们将子类化并将任何未初始化的实例变量添加到子类中,我们就停止了继承初始值设定项。在我们添加自己的初始值设定项之前,编译器会对我们大喊大叫

    需要明确的是,未初始化的实例变量是未给定默认值的任何实例变量(请记住,选项和隐式展开的选项自动假定默认值为
    nil

    因此,在这种情况下:

    class Foo {
        var a: Int
    }
    
    a
    是未初始化的实例变量。除非我们给
    a
    一个默认值,否则这不会编译:

    class Foo {
        var a: Int = 0
    }
    
    或在初始化器方法中初始化
    a

    class Foo {
        var a: Int
    
        init(a: Int) {
            self.a = a
        }
    }
    
    现在,让我们看看如果我们将
    Foo
    子类化会发生什么,好吗

    class Bar: Foo {
        var b: Int
    
        init(a: Int, b: Int) {
            self.b = b
            super.init(a: a)
        }
    }
    
    对吧??我们添加了一个变量,并添加了一个初始值设定项,将值设置为
    b
    ,这样它就可以编译了。根据您所使用的语言,您可能希望
    Bar
    继承了
    Foo
    的初始值设定项
    init(a:Int)
    。但事实并非如此。怎么可能呢?
    Foo
    init(a:Int)
    如何为添加的
    Bar
    变量
    b
    赋值?没有。因此,我们无法使用无法初始化所有值的初始值设定项来初始化
    Bar
    实例

    这些与便利性有什么关系

    那么我们来看看,

    规则1

    如果您的子类没有定义任何指定的初始值设定项,它将自动继承其所有超类指定的初始值设定项

    规则2

    如果您的子类通过按照规则1继承其所有超类指定的初始值设定项,或者通过提供自定义实现作为其定义的一部分,来提供其所有超类指定初始值设定项的实现,那么它将自动继承所有超类便利初始值设定项

    请注意规则2,其中提到便利初始值设定项

    因此,
    便利性
    关键字所做的是向我们指出哪些初始值设定项可以被添加了实例变量而没有默认值的子类继承

    让我们以这个例子
    Base
    class:

    class Base {
        let a: Int
        let b: Int
    
        init(a: Int, b: Int) {
            self.a = a
            self.b = b
        }
    
        convenience init() {
            self.init(a: 0, b: 0)
        }
    
        convenience init(a: Int) {
            self.init(a: a, b: 0)
        }
    
        convenience init(b: Int) {
            self.init(a: 0, b: b)
        }
    }
    
    请注意,这里有三个
    便利性
    初始值设定项。这意味着我们有三个可以继承的初始值设定项。我们有一个指定的初始值设定项(一个指定的初始值设定项)