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

前端之家收集整理的这篇文章主要介绍了为什么在Swift中甚至需要便利关键字?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
由于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")
    }
}
现有的答案只讲一半的便利故事。另一半的故事,没有一个现有的答案覆盖,回答了问题Desmond发表在评论

Why would Swift force me to put convenience in front of my initializer just because I need to call self.init from it?`

我在this answer年轻微触及它,其中我详细地覆盖了Swift的初始化规则,但主要关注的是所需的词。但是,这个答案仍然涉及与这个问题和这个答案相关的东西。我们必须了解Swift初始化程序继承的工作原理。

因为Swift不允许未初始化的变量,所以不能保证从你继承的类继承所有(或任何)初始化器。如果我们子类化并添加任何未初始化的实例变量到我们的子类,我们停止继承初始化。直到我们添加我们自己的初始化器,编译器将对我们喊。

要清楚,未初始化的实例变量是没有给出默认值的任何实例变量(请记住,可选项和隐含地解开的可选项自动采用默认值nil)。

所以在这种情况下:

class Foo {
    var a: Int
}

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实例。

这与方便有什么关系?

好吧,让我们来看看the rules on initializer inheritance

Rule 1

If your subclass doesn’t define any designated initializers,it automatically inherits all of its superclass designated initializers.

Rule 2

If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1,or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

注意规则2,其中提到了便利初始化。

所以方便关键字做的是告诉我们哪些初始化器可以被添加没有默认值的实例变量的子类继承。

让我们看一下这个例子Base类:

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)
    }
}

注意,我们在这里有三个方便初始化器。这意味着我们有三个可以继承的初始化器。我们有一个指定的初始化器(指定的初始化器只是不是方便初始化器的任何初始化器)。

我们可以通过四种不同的方式实例化基类的实例:

所以,让我们创建一个子类。

class NonInheritor: Base {
    let c: Int

    init(a: Int,b: Int,c: Int) {
        self.c = c
        super.init(a: a,b: b)
    }
}

我们继承自Base。我们添加了我们自己的实例变量,我们没有给它一个默认值,所以我们必须添加我们自己的初始化。我们添加了一个init(a:Int,b:Int,c:Int),但它不匹配Base类的指定初始化器的签名:init(a:Int,b:Int)。这意味着,我们不会从Base继承任何初始化:

所以,如果我们继承Base,会发生什么,但我们继续和实现一个初始化器匹配指定的初始化器从Base?

class Inheritor: Base {
    let c: Int

    init(a: Int,b: b)
    }

    convenience override init(a: Int,b: Int) {
        self.init(a: a,b: b,c: 0)
    }
}

现在,除了我们在这个类中直接实现的两个初始化器之外,因为我们实现了一个匹配Base类的指定初始化器的初始化器,所以我们继承了所有Base类的方便初始化器:

具有匹配签名的初始化器被标记为方便的事实在这里没有差别。它只意味着Inheritor只有一个指定的初始化器。因此,如果我们从Inheritor继承,我们只需要实现一个指定的初始化器,然后我们继承Inheritor的方便初始化器,这反过来意味着我们实现了所有的Base的指定的初始化器,并且可以继承它的方便初始化器。

猜你在找的Swift相关文章