Why convenience keyword is even needed in Swift?(为什么在 Swift 中甚至需要方便关键字?)
问题描述
由于 Swift 支持方法和初始化器重载,您可以将多个 init
并排放置,并使用您认为方便的任何一个:
类人{变量名称:字符串初始化(名称:字符串){self.name = 名称}在里面() {self.name = "约翰"}}
那么为什么会有 convenience
关键字存在呢?是什么让以下内容变得更好?
类人{变量名称:字符串初始化(名称:字符串){self.name = 名称}方便初始化(){self.init(名称:约翰")}}
现有的答案只讲述了 convenience
故事的一半.故事的另一半,即现有答案都没有涵盖的那一半,回答了戴斯蒙德在评论中发布的问题:
为什么 Swift 仅仅因为我需要从中调用 self.init
就强迫我将 convenience
放在初始化程序前面?`
我在
所以,让我们创建一个子类.
class NonInheritor: Base {让 c: 诠释初始化(a:Int,b:Int,c:Int){自我.c = c超级初始化(a:a,b:b)}}
我们继承自 Base
.我们添加了我们自己的实例变量并且我们没有给它一个默认值,所以我们必须添加我们自己的初始化程序.我们添加了一个 init(a: Int, b: Int, c: Int)
,但它与 Base
类的指定初始化器的签名不匹配:init(a:Int,b:Int)
.这意味着,我们没有从 Base
继承 any 初始化器:
那么,如果我们从 Base
继承,但我们继续实现了一个与 Base
中指定的初始化程序相匹配的初始化程序,会发生什么?
类继承者:Base {让 c: 诠释初始化(a:Int,b:Int,c:Int){自我.c = c超级初始化(a:a,b:b)}便利覆盖 init(a: Int, b: Int) {self.init(a:a,b:b,c:0)}}
现在,除了我们在这个类中直接实现的两个初始化器之外,因为我们实现了一个与Base
类指定初始化器匹配的初始化器,我们可以继承所有Base
类的 convenience
初始化器:
具有匹配签名的初始化程序被标记为 convenience
的事实在这里没有区别.这只意味着 Inheritor
只有一个指定的初始化器.因此,如果我们从 Inheritor
继承,我们只需要实现一个指定的初始化器,然后我们将继承 Inheritor
的便利初始化器,这反过来意味着我们'已经实现了所有 Base
的指定初始化器并且可以继承它的 convenience
初始化器.
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"
}
}
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")
}
}
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:
Why would Swift force me to put
convenience
in front of my initializer just because I need to callself.init
from it?`
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.
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.
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
).
So in this case:
class Foo {
var a: Int
}
a
is an uninitialized instance variable. This will not compile unless we give a
a default value:
class Foo {
var a: Int = 0
}
or initialize a
in an initializer method:
class Foo {
var a: Int
init(a: Int) {
self.a = a
}
}
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)
}
}
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.
What does any of this have to do with convenience
?
Well, let's look at 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.
Notice Rule 2, which mentions convenience initializers.
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.
Let's take this example 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)
}
}
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)
}
}
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
:
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)
}
}
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:
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 中甚至需要方便关键字?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:为什么在 Swift 中甚至需要方便关键字?
- GPS状态的广播接收器? 2022-01-01
- UITextView 内容插图 2022-01-01
- SetOnItemSelectedListener上的微调程序错误 2022-01-01
- 网上有没有好的 UIScrollView 教程? 2022-01-01
- 使用自动布局向 UIScrollView 添加动态大小的视图 2022-01-01
- 在 Iphone SDK 的导航栏上添加多个按钮 2022-01-01
- URL编码Swift iOS 2022-01-01
- Xcode 7.3 中带有 UILabel 的 UIStackView 2022-01-01
- 如何在 iPhone 模拟器中重置 NSUserDefaults 数据? 2022-01-01
- 类似于 Mail.app 的 iPad 模态视图控制器? 2022-01-01