为什么在Swift中甚至需要便捷关键字? [英] Why convenience keyword is even needed in Swift?

查看:90
本文介绍了为什么在Swift中甚至需要便捷关键字?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于Swift支持方法和初始化程序重载,因此您可以将多个init放在一起,并使用您认为方便的任何方式:

Since Swift supports method and initializer overloading, you can put multiple init alongside each other and use whichever you deem convenient:

class Person {
    var name:String

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

    init() {
        self.name = "John"
    }
}

那为什么convenience关键字甚至会存在呢?是什么使以下内容显着改善?

So why would convenience keyword even exist? What makes the following substantially better?

class Person {
    var name:String

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

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

推荐答案

现有答案仅说明了convenience故事的一半.故事的另一半(现有答案中没有涵盖的另一半)回答了戴斯蒙德在评论中发布的问题:

The existing answers only tell half of the convenience story. The other half of the story, the half that none of the existing answers cover, answers the question Desmond has posted in the comments:

为什么Swift会因为我需要从其调用self.init而迫使我将convenience放在初始化程序的前面?

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

我在此答案中略微提及了该内容,在其中我涵盖了Swift的几个初始值设定项有详细规定,但主要重点是required字.但是该答案仍在解决与该问题和该答案相关的问题.我们必须了解Swift初始化程序继承的工作原理.

I touched on it slightly in this answer, in which I cover several of Swift's initializer rules in details, but the main focus there was on the required word. But that answer was still addressing something that's relevant to this question and this answer. We have to understand how Swift initializer inheritance works.

因为Swift不允许使用未初始化的变量,所以不能保证从您继承的类继承所有(或任何)初始化器.如果我们将其子类化并向其子类中添加任何未初始化的实例变量,那么我们将停止继承初始化器.直到我们添加自己的初始化程序之前,编译器都会对我们大喊大叫.

Because Swift does not allow for uninitialized variables, you are not guaranteed to inherit all (or any) initializers from the class you inherit from. If we subclass and add any uninitialized instance variables to our subclass, we have stopped inheriting initializers. And until we add initializers of our own, the compiler will yell at us.

需要明确的是,未初始化的实例变量是任何未赋予默认值的实例变量(请注意,可选变量和隐式展开的可选变量会自动采用默认值nil).

To be clear, an uninitialized instance variable is any instance variable which isn't given a default value (keeping in mind that optionals and implicitly unwrapped optionals automatically assume a default value of nil).

所以在这种情况下:

class Foo {
    var a: Int
}

a是未初始化的实例变量.除非我们给a一个默认值,否则它将不会编译:

a is an uninitialized instance variable. This will not compile unless we give a a default value:

class Foo {
    var a: Int = 0
}

或使用初始化方法初始化a:

or initialize a in an initializer method:

class Foo {
    var a: Int

    init(a: Int) {
        self.a = a
    }
}

现在,让我们看看如果将Foo子类化,会发生什么情况?

Now, let's see what happens if we subclass Foo, shall we?

class Bar: Foo {
    var b: Int

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

对吗?我们添加了一个变量,并添加了一个初始化程序以将值设置为b以便进行编译.根据您使用的语言,您可能希望Bar继承了Foo的初始化程序init(a: Int).但事实并非如此.怎么可能呢? Fooinit(a: Int)如何知道如何为Bar添加的b变量分配值?没有.因此,我们无法使用无法初始化我们所有值的初始化器来初始化Bar实例.

Right? We added a variable, and we added an initializer to set a value to b so it'll compile. Depending on what language you're coming from, you might expect that Bar has inherited Foo's initializer, init(a: Int). But it doesn't. And how could it? How does Foo's init(a: Int) know how to assign a value to the b variable that Bar added? It doesn't. So we can't initialize a Bar instance with an initializer that can't initialize all of our values.

这与convenience有什么关系?

好吧,我们来看一下关于初始化程序继承的规则:

规则1

如果您的子类未定义任何指定的初始化器,它将自动继承其所有超类的指定初始化器.

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

规则2

如果您的子类提供了其所有超类指定的初始化器的实现(通过按规则1继承它们,或通过提供自定义实现作为其定义的一部分),则它将自动继承所有超类的便利初始化器. /p>

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,其中提到了便利初始化程序.

Notice Rule 2, which mentions convenience initializers.

因此,convenience关键字 所做的事情是向我们表明哪些初始化器可以被添加没有默认值的实例变量的子类继承.

So what the convenience keyword does do is indicate to us which initializers can be inherited by subclasses that add instance variables without default values.

让我们以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)
    }
}

请注意,这里有三个convenience初始值设定项.这意味着我们有三个可以继承的初始化程序.我们有一个指定的初始化程序(一个指定的初始化程序就是任何不是便利初始化程序的初始化程序).

Notice we have three convenience initializers here. That means we have three initializers that can be inherited. And we have one designated initializer (a designated initializer is simply any initializer that's not a convenience initializer).

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

We can instantiate instances of the base class in four different ways:

因此,让我们创建一个子类.

So, let's create a subclass.

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继承 any 初始化程序:

We're inheriting from Base. We added our own instance variable and we didn't give it a default value, so we must add our own initializers. We added one, init(a: Int, b: Int, c: Int), but it doesn't match the signature of the Base class's designated initializer: init(a: Int, b: Int). That means, we're not inheriting any initializers from Base:

那么,如果我们继承自Base,会发生什么,但是我们继续实施了与Base中指定的初始值设定项相匹配的初始值设定项?

So, what would happen if we inherited from Base, but we went ahead and implemented an initializer that matched the designated initializer from Base?

class Inheritor: Base {
    let c: Int

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

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

现在,除了我们在此类中直接实现的两个初始化程序之外,由于我们实现了与Base类的指定初始化程序匹配的初始化程序,因此我们继承了所有Base类的convenience初始化程序:

Now, in addition to the two initializers we implemented directly in this class, because we implemented an initializer matching Base class's designated initializer, we get to inherit all of Base class's convenience initializers:

具有匹配签名的初始化程序标记为convenience的事实在这里没有区别.这仅表示Inheritor仅具有一个指定的初始化程序.因此,如果我们从Inheritor继承,我们只需要实现一个指定的初始化程序,然后我们继承Inheritor的便捷初始化程序,这又意味着我们已经实现了所有Base的指定的初始值设定项,并且可以继承其convenience初始值设定项.

The fact that the initializer with the matching signature is marked as convenience makes no difference here. It only means that Inheritor has just one designated initializer. So if we inherit from Inheritor, we'd just have to implement that one designated initializer, and then we'd inherit Inheritor's convenience initializer, which in turn means we've implemented all of Base's designated initializers and can inherit its convenience initializers.

这篇关于为什么在Swift中甚至需要便捷关键字?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆