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") } }
Why would Swift force me to put
convenience
in front of my initializer just because I need to callself.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的指定的初始化器,并且可以继承它的方便初始化器。