★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ➤微信公众号:山青咏芝(shanqingyongzhi) ➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/) ➤GitHub地址:https://github.com/strengthen/LeetCode ➤原文地址:https://www.cnblogs.com/strengthen/p/9739365.html ➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。 ➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创! ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
热烈欢迎,请直接点击!!!
进入博主App Store主页,下载使用各个作品!!!
注:博主将坚持每月上线一个新app!!!
初始化是准备使用的类,结构或枚举实例的过程。此过程涉及为该实例上的每个存储属性设置初始值,并执行新实例准备使用之前所需的任何其他设置或初始化。
您可以通过定义初始值设定项来实现此初始化过程,初始值设定项类似于可以调用以创建特定类型新实例的特殊方法。与Objective-C初始值设定项不同,Swift初始值设定项不会返回值。它们的主要作用是确保在首次使用类型之前,正确初始化类型的新实例。
类类型的实例还可以实现一个deinitializer,该方法将在释放该类的实例之前执行任何自定义清除。有关反初始化程序的更多信息,请参见反初始化。
设置存储属性的初始值
类和结构必须在创建该类或结构的实例时将其所有存储的属性设置为适当的初始值。存储的属性不能处于不确定状态。
您可以在初始化程序中为存储的属性设置初始值,或通过将默认属性值分配为属性定义的一部分来设置初始值。以下各节介绍了这些操作。
笔记
当您为存储的属性分配默认值,或在初始化程序中设置其初始值时,将直接设置该属性的值,而无需调用任何属性观察器。
初始化器
调用初始化程序以创建特定类型的新实例。最简单的形式是,初始化程序就像没有参数的实例方法,使用init 关键字编写:
- init() {
- // perform some initialization here
- }
下面的示例定义了一个新结构,称为Fahrenheit 存储以华氏度表示的温度。该Fahrenheit 结构具有一个存储的属性temperature ,其类型为Double :
- struct Fahrenheit {
- var temperature: Double
- init() {
- temperature = 32.0
- }
- }
- var f = Fahrenheit()
- print("The default temperature is \(f.temperature)° Fahrenheit")
- // Prints "The default temperature is 32.0° Fahrenheit"
该结构定义了一个init 没有参数的初始化程序,该初始化程序使用值32.0 (水的冰点,以华氏度为单位)来初始化存储的温度。
默认属性值
您可以从初始化程序中设置存储属性的初始值,如上所示。或者,指定默认属性值作为属性声明的一部分。您可以通过在定义属性时为其分配初始值来指定默认属性值。
笔记
如果属性始终使用相同的初始值,请提供默认值,而不要在初始化程序中设置值。最终结果是相同的,但是默认值将属性的初始化与声明更紧密地联系在一起。它使初始化程序更短,更清晰,并使您能够从其默认值推断属性的类型。默认值还使您更容易利用默认初始化程序和初始化程序继承,如本章稍后所述。
您可以Fahrenheit 通过temperature 在声明属性时为其属性提供默认值,以更简单的形式从上方编写该结构:
- struct Fahrenheit {
- var temperature = 32.0
- }
自定义初始化
您可以使用输入参数和可选属性类型,或通过在初始化过程中分配常量属性来自定义初始化过程,如以下各节所述。
初始化参数
您可以提供初始化参数作为初始化程序定义的一部分,以定义自定义初始化过程的值的类型和名称。初始化参数具有与功能和方法参数相同的功能和语法。
以下示例定义了一个名为的结构Celsius ,该结构存储以摄氏度表示的温度。该Celsius 结构实现了两个自定义的初始化程序,称为init(fromFahrenheit:) 和init(fromKelvin:) ,它们使用不同温度范围内的值初始化该结构的新实例:
- struct Celsius {
- var temperatureInCelsius: Double
- init(fromFahrenheit fahrenheit: Double) {
- temperatureInCelsius = (fahrenheit - 32.0) / 1.8
- }
- init(fromKelvin kelvin: Double) {
- temperatureInCelsius = kelvin - 273.15
- }
- }
- let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
- // boilingPointOfWater.temperatureInCelsius is 100.0
- let freezingPointOfWater = Celsius(fromKelvin: 273.15)
- // freezingPointOfWater.temperatureInCelsius is 0.0
第一个初始化程序具有单个初始化参数,其参数标签为fromFahrenheit ,参数名称为fahrenheit 。第二个初始化程序具有一个初始化参数,其参数标签为fromKelvin ,参数名称为kelvin 。两个初始化程序都将其单个参数转换为相应的摄氏度值,并将此值存储在名为的属性中temperatureInCelsius 。
参数名称和参数标签
与函数和方法参数一样,初始化参数可以具有在初始化程序的主体内使用的参数名称和在调用初始化程序时使用的参数标签。
但是,初始化函数在函数前没有括号,以函数和方法的方式标识函数名。因此,初始化器参数的名称和类型在确定应调用哪个初始化器中起着特别重要的作用。因此,如果您不提供初始化方法,则Swift会为初始化程序中的每个参数提供一个自动参数标签。
下面的例子定义了一个名为结构Color ,具有三个恒定属性叫做red ,green ,和blue 。这些属性存储一个介于0.0 和之间的值,1.0 以指示颜色中红色,绿色和蓝色的数量。
Color 为初始化器Double 的红色,绿色和蓝色分量提供三个适当命名的类型的参数。Color 还提供了带有单个white 参数的第二个初始化器,该初始化器用于为所有三个颜色分量提供相同的值。
- struct Color {
- let red, green, blue: Double
- init(red: Double, green: Double, blue: Double) {
- self.red = red
- self.green = green
- self.blue = blue
- }
- init(white: Double) {
- red = white
- green = white
- blue = white
- }
- }
Color 通过为每个初始值设定项参数提供命名值,可以使用这两个初始值设定项来创建新实例:
- let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
- let halfGray = Color(white: 0.5)
请注意,不使用参数标签就无法调用这些初始化程序。如果已定义参数标签,则必须始终在初始化程序中使用它们,而忽略它们是编译时错误:
- let veryGreen = Color(0.0, 1.0, 0.0)
- // this reports a compile-time error - argument labels are required
不带参数标签的初始化参数
如果不想为初始化参数使用参数标签,请为该参数写下划线(_ )而不是显式参数标签,以覆盖默认行为。
这是上述“初始化参数”中Celsius 示例的扩展版本,并带有一个附加的初始化程序,该初始化程序可根据已经在摄氏度范围内的值创建一个新实例:Celsius Double
- struct Celsius {
- var temperatureInCelsius: Double
- init(fromFahrenheit fahrenheit: Double) {
- temperatureInCelsius = (fahrenheit - 32.0) / 1.8
- }
- init(fromKelvin kelvin: Double) {
- temperatureInCelsius = kelvin - 273.15
- }
- init(_ celsius: Double) {
- temperatureInCelsius = celsius
- }
- }
- let bodyTemperature = Celsius(37.0)
- // bodyTemperature.temperatureInCelsius is 37.0
初始化调用Celsius(37.0) 的意图很明确,不需要参数标签。因此,应将此初始化程序编写为,以便可以通过提供未命名的值来调用它。init(_ celsius: Double) Double
可选属性类型
如果您的自定义类型具有在逻辑上允许“无值”的存储属性(可能是因为在初始化过程中无法设置其值,或者是因为稍后允许使用“无值”),请使用可选类型。可选类型的属性将使用值自动初始化nil ,表明该属性在初始化过程中故意具有“没有值”。
以下示例定义了一个名为的类SurveyQuestion ,该类具有一个可选String 属性response :
- class SurveyQuestion {
- var text: String
- var response: String?
- init(text: String) {
- self.text = text
- }
- func ask() {
- print(text)
- }
- }
- let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
- cheeseQuestion.ask()
- // Prints "Do you like cheese?"
- cheeseQuestion.response = "Yes, I do like cheese."
直到询问问题后,才能知道对调查问题的回答,因此,该response 属性使用String? 或“ optional String ”类型声明。初始化nil 新实例时,SurveyQuestion 会自动为其指定默认值,表示“没有字符串” 。
在初始化期间分配常量属性
您可以在初始化期间的任何时候为常量属性分配一个值,只要在初始化完成时将其设置为确定的值即可。为常量属性分配值后,就无法再对其进行修改。
笔记
对于类实例,只能在引入常量的类的初始化期间对其进行修改。子类不能修改它。
您可以SurveyQuestion 从上面修改示例,以text 对问题的属性使用常量属性,而不是变量属性,以表明一旦SurveyQuestion 创建的实例,问题就不会更改。即使该text 属性现在是一个常量,仍可以在类的初始化程序中对其进行设置:
- class SurveyQuestion {
- let text: String
- var response: String?
- init(text: String) {
- self.text = text
- }
- func ask() {
- print(text)
- }
- }
- let beetsQuestion = SurveyQuestion(text: "How about beets?")
- beetsQuestion.ask()
- // Prints "How about beets?"
- beetsQuestion.response = "I also like beets. (But not with cheese.)"
默认初始化器
迅速提供了一个默认初始值对于所有其属性提供缺省值,并且不提供至少一个初始值设定本身的任何结构或类。默认初始化程序仅创建一个新实例,并将其所有属性设置为其默认值。
此示例定义了一个名为的类ShoppingListItem ,该类将购物清单中商品的名称,数量和购买状态封装起来:
- class ShoppingListItem {
- var name: String?
- var quantity = 1
- var purchased = false
- }
- var item = ShoppingListItem()
因为ShoppingListItem 该类的所有属性都具有默认值,并且因为它是没有超类的基类,所以它会ShoppingListItem 自动获得一个默认的初始化器实现,该实现会创建一个将其所有属性设置为其默认值的新实例。(该name 属性是可选String 属性,因此nil 即使该值未写在代码中,该属性也会自动接收默认值。)上面的示例使用ShoppingListItem 该类的默认初始化程序来创建带有的新类实例。初始化语法,写为ShoppingListItem() ,并将此新实例分配给名为的变量item 。
结构类型的成员初始化器
如果结构类型未定义任何自己的自定义初始化程序,则它们会自动收到一个成员初始化程序。与默认初始化程序不同,该结构即使在存储了没有默认值的属性的情况下,也会接收成员初始化程序。
逐成员初始化器是初始化新结构实例的成员属性的简便方法。可以通过名称将新实例的属性的初始值传递给成员初始化器。
以下示例定义了一个结构Size ,该结构具有两个称为width 和的属性height 。Double 通过指定默认值,可以推断这两个属性均为类型0.0 。
该Size 结构会自动接收一个init(width:height:) 成员级初始化程序,您可以使用该初始化程序来初始化新Size 实例:
- struct Size {
- var width = 0.0, height = 0.0
- }
- let twoByTwo = Size(width: 2.0, height: 2.0)
调用成员初始化程序时,可以忽略具有默认值的任何属性的值。在上面的示例中,该Size 结构的height 和width 属性均具有默认值。您可以省略一个属性或两个属性,并且初始化程序将对所有省略的内容使用默认值,例如:
- let zeroByTwo = Size(height: 2.0)
- print(zeroByTwo.width, zeroByTwo.height)
- // Prints "0.0 2.0"
-
- let zeroByZero = Size()
- print(zeroByZero.width, zeroByZero.height)
- // Prints "0.0 0.0"
值类型的初始化程序委托
初始化程序可以调用其他初始化程序来执行实例初始化的一部分。此过程称为初始化程序委托,可避免在多个初始化程序之间重复代码。
对于值类型和类类型,初始化程序委派的工作方式以及允许哪种形式的委派的规则是不同的。值类型(结构和枚举)不支持继承,因此它们的初始化程序委托过程相对简单,因为它们只能委托给自己提供的另一个初始化程序。但是,类可以从其他类继承,如Inheritance中所述。这意味着类还有其他责任,以确保在初始化期间为它们继承的所有存储属性分配适当的值。这些职责在下面的类继承和初始化中进行了描述。
对于值类型,self.init 在编写自己的自定义初始值设定项时,通常会引用同一值类型的其他初始值设定项。您self.init 只能在初始化程序中调用。
请注意,如果您为值类型定义自定义初始化程序,则将无法再使用该类型的默认初始化程序(或成员初始化程序,如果它是结构)。该约束防止了使用自动初始化程序之一的人意外绕过更复杂的初始化程序中提供的其他基本设置的情况。
笔记
如果您希望自定义值类型可以使用默认的初始化器和成员初始化器以及您自己的自定义初始化器进行初始化,请在扩展名中编写自定义初始化器,而不是将其作为值类型的原始实现的一部分。有关更多信息,请参见扩展。
下面的示例定义一个自定义Rect 结构来表示一个几何矩形。该示例需要两个称为Size 和的支持结构Point ,这两个结构都0.0 为其所有属性提供默认值:
- struct Size {
- var width = 0.0, height = 0.0
- }
- struct Point {
- var x = 0.0, y = 0.0
- }
您可以通过Rect 以下三种方式之一来初始化下面的结构:使用其默认的零初始化值origin 和size 属性值,提供特定的原点和尺寸,或提供特定的中心点和尺寸。这些初始化选项由三个自定义初始化器表示,它们是Rect 结构定义的一部分:
- struct Rect {
- var origin = Point()
- var size = Size()
- init() {}
- init(origin: Point, size: Size) {
- self.origin = origin
- self.size = size
- }
- init(center: Point, size: Size) {
- let originX = center.x - (size.width / 2)
- let originY = center.y - (size.height / 2)
- self.init(origin: Point(x: originX, y: originY), size: size)
- }
- }
如果结构没有自己的自定义初始化Rect 程序,init() 则第一个初始化程序,在功能上与该结构将收到的默认初始化程序相同。此初始值设定项的主体为空,由一对空的花括号表示{} 。调用此初始值设定项将返回一个Rect 实例,该实例的origin 和size 属性均使用和从其属性定义的默认值初始化:Point(x: 0.0, y: 0.0) Size(width: 0.0, height: 0.0)
- let basicRect = Rect()
- // basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)
第二个Rect 初始化程序,init(origin:size:) 在功能上与结构不具有自己的自定义初始化程序时将接收的成员初始化程序相同。此初始化程序仅将origin 和size 参数值分配给适当的存储属性:
- let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
- size: Size(width: 5.0, height: 5.0))
- // originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0)
第三个Rect 初始化init(center:size:) 器稍微复杂一些。首先根据一个center 点和一个size 值计算一个合适的原点。然后,它调用(或委托)init(origin:size:) 初始化器,该初始化器将新的origin和size值存储在适当的属性中:
- let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
- size: Size(width: 3.0, height: 3.0))
- // centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
在init(center:size:) 初始化可能分配的新值origin ,并size 以相应的属性本身。但是,对于init(center:size:) 初始化程序而言,利用已经提供了该功能的现有初始化程序更方便(意图更清晰)。
笔记
有关无需自行定义init() 和init(origin:size:) 初始化程序的另一示例编写方式,请参见Extensions。
类继承和初始化
在初始化期间,必须为类的所有存储属性(包括该类从其超类继承的所有属性)分配一个初始值。
Swift为类类型定义了两种初始化器,以帮助确保所有存储的属性都接收初始值。这些被称为指定的初始值设定项和便利性初始值设定项。
指定的初始化程序和便利性初始化程序
指定的初始值设定项是类的主要初始值设定项。指定的初始化程序将完全初始化该类引入的所有属性,并调用适当的超类初始化程序以继续超类链中的初始化过程。
类往往只有很少的指定初始化器,而一个类只有一个很常见。指定的初始化程序是“漏斗”点,通过该“漏斗”点进行初始化,并通过该“漏斗”点继续超类链中的初始化过程。
每个类必须至少有一个指定的初始化程序。在某些情况下,可以通过从超类继承一个或多个指定的初始化程序来满足此要求,如下面的“自动初始化程序继承”中所述。
便利的初始值设定项是辅助的,支持类的初始值设定项。您可以定义一个便捷初始化程序,以从与便捷初始化程序相同的类中调用一个指定初始化程序,并将某些指定初始值设定项的参数设置为默认值。您还可以定义一个便捷初始化程序,以针对特定用例或输入值类型创建该类的实例。
如果您的班级不需要便利初始化器,则不必提供它们。只要通向通用初始化模式的快捷方式可以节省时间或使类的初始化更清晰,就可以创建方便的初始化器。
指定和便捷初始化程序的语法
指定的类初始值设定项的编写方式与值类型的简单初始值设定项的编写方式相同:
- init(parameters) {
- statements
- }
便捷初始化程序以相同的样式编写,但convenience 修饰符放在init 关键字之前,并用空格分隔:
- convenience init(parameters) {
- statements
- }
类类型的初始化程序委托
为了简化指定的初始化器和便捷的初始化器之间的关系,Swift将以下三个规则应用于初始化器之间的委托调用:
- 规则1
- 指定的初始值设定项必须从其直接超类调用指定的初始值设定项。
- 规则二
-
便捷初始化程序必须从同一类调用另一个初始化程序。
- 规则三
- 便利初始化程序必须最终调用指定的初始化程序。
记住这一点的一种简单方法是:
-
指定的初始值必须始终委派了。
-
便利的初始化必须始终委派跨越。
这些规则如下图所示:
在这里,超类具有一个指定的初始值设定项和两个便利的初始值设定项。一个便利初始化程序调用另一个便利初始化程序,后者又调用单个指定的初始化程序。这从上方满足规则2和3。超类本身没有其他超类,因此规则1不适用。
该图中的子类具有两个指定的初始化程序和一个便捷的初始化程序。便捷初始化程序必须调用两个指定的初始化程序之一,因为它只能调用同一类中的另一个初始化程序。这从上方满足规则2和3。两个指定的初始值设定项都必须从超类中调用单个指定的初始值设定项,以满足上方的规则1。
笔记
这些规则不会影响您的类的用户如何创建每个类的实例。上图中的任何初始化程序都可用于创建它们所属类的完全初始化的实例。规则仅影响您编写类的初始化程序的实现的方式。
下图显示了四个类的更复杂的类层次结构。它说明了此层次结构中指定的初始化程序如何充当类初始化的“漏斗”点,从而简化了链中各类之间的相互关系:
两阶段初始化
Swift中的类初始化是一个分为两个阶段的过程。在第一阶段,每个存储的属性都由引入它的类分配一个初始值。一旦确定了每个存储属性的初始状态,便开始第二阶段,并且在认为新实例可以使用之前,每个类都有机会自定义其存储属性。
两阶段初始化过程的使用使初始化安全,同时仍为类层次结构中的每个类提供了完全的灵活性。两阶段初始化可防止在初始化属性值之前对其进行访问,并防止其他初始化程序意外地将属性值设置为其他值。
笔记
Swift的两阶段初始化过程类似于Objective-C中的初始化。主要区别在于,在阶段1中,Objective-C为每个属性分配零或空值(例如0 或nil )。Swift的初始化流程更加灵活,因为它可以让您设置自定义初始值,并且可以处理有效值0 或nil 无效值的类型。
Swift的编译器执行四项有用的安全检查,以确保两阶段初始化完成且没有错误:
- 安全检查1
- 指定的初始值设定项必须确保由其类引入的所有属性在委托给超类初始值设定项之前都已初始化。
如上所述,仅在知道对象所有存储属性的初始状态后,才认为该对象的内存已完全初始化。为了满足此规则,指定的初始值设定项必须确保在传递链之前初始化其自身的所有属性。
- 安全检查2
- 在将值分配给继承的属性之前,指定的初始值设定项必须委托一个超类初始值设定项。如果不是这样,则指定的初始化器分配的新值将被超类覆盖,作为其自身初始化的一部分。
- 安全检查3
-
便利初始化程序必须在将值分配给任何属性(包括由同一类定义的属性)之前委托给另一个初始化程序。如果不是,便利初始化程序分配的新值将被其自己班级指定的初始化程序覆盖。
- 安全检查4
-
初始化
self 的第一阶段完成之后,初始化程序才能调用任何实例方法,读取任何实例属性的值或将其称为值。
在第一阶段结束之前,该类实例并不完全有效。一旦在第一阶段结束时知道类实例是有效的,就只能访问属性,并且只能调用方法。
根据上述四个安全检查,以下是两阶段初始化的执行过程:
阶段1
- 在类上调用了指定的或便捷的初始化程序。
- 分配了该类的新实例的内存。内存尚未初始化。
- 该类的指定初始化程序确认该类引入的所有存储的属性都具有一个值。这些存储的属性的内存现在已初始化。
- 指定的初始值设定项移交给超类初始值设定项,以为其自身的存储属性执行相同的任务。
- 这将继续类继承链,直到到达链的顶部。
- 一旦到达链的顶部,并且链中的最后一个类已确保其所有存储的属性都具有值,则实例的内存被视为已完全初始化,并且阶段1已完成。
阶段2
-
从链的顶部向下追溯,链中的每个指定的初始化程序都可以选择进一步自定义实例。初始化程序现在可以访问
self 并可以修改其属性,调用其实例方法等等。
-
最后,链中的所有便利初始化程序都可以选择自定义实例并与一起使用
self 。
以下是阶段1寻找假设的子类和超类的初始化调用的方式:
在此示例中,初始化始于对子类的便捷初始化程序的调用。此便利的初始值设定项尚不能修改任何属性。它委托来自同一类的指定初始化器。
根据安全检查1,指定的初始化器确保子类的所有属性都有一个值。然后,它在其父类上调用指定的初始化器,以继续进行链上的初始化。
超类的指定初始化器确保所有超类属性都有一个值。没有其他可初始化的超类,因此不需要进一步的委派。
一旦超类的所有属性都具有初始值,就将其内存视为已完全初始化,并且阶段1已完成。
以下是第2阶段寻找相同初始化调用的方式:
现在,超类的指定初始化器有机会进一步自定义实例(尽管不必如此)。
一旦超类的指定初始化器完成,子类的指定初始化器就可以执行其他自定义操作(尽管再次,它不必这样做)。
最后,一旦子类的指定初始化程序完成,最初调用的便捷初始化程序就可以执行其他自定义。
初始化程序的继承和覆盖
与Objective-C中的子类不同,Swift子类默认情况下不会继承其超类初始化器。Swift的方法可防止出现以下情况:超类中的简单初始化程序被更专门的子类继承,并用于创建未完全或正确初始化的子类的新实例。
笔记
超类初始化程序在某些情况下会被继承,但是只有在安全且适当的情况下才可以这样做。有关更多信息,请参见下面的自动初始化继承。
如果希望自定义子类提供与其父类相同的一个或多个相同的初始化器,则可以在子类中提供这些初始化器的自定义实现。
当编写与超类指定的初始值设定项匹配的子类初始值设定项时,实际上是在提供该指定的初始值设定项的替代。因此,必须override 在子类的初始化程序定义之前编写修饰符。即使您要覆盖自动提供的默认初始化程序,这也是正确的,如Default Initializers中所述。
与覆盖属性,方法或下标一样,override 修饰符的存在会提示Swift检查超类是否具有匹配的指定初始化器要被覆盖,并验证是否已按预期指定了覆盖初始化器的参数。
笔记
override 重写超类指定的初始化程序时,即使您的子类对初始化程序的实现是便捷的初始化程序,也始终要编写修饰符。
相反,如果您编写与超类便利性初始化程序匹配的子类初始化程序,则根据上面的“类类型的初始化程序委托”中所述的规则,您的子类将永远无法直接调用该超类便利性初始化程序。因此,您的子类(严格地说)没有提供超类初始值设定项的替代。结果,override 在提供超类便捷初始化程序的匹配实现时,您无需编写修饰符。
下面的示例定义了一个名为的基类Vehicle 。此基类声明一个称为的存储属性numberOfWheels ,默认Int 值为0 。该numberOfWheels 属性由计算的属性使用,该属性称为description 来创建String 车辆特性的描述:
- class Vehicle {
- var numberOfWheels = 0
- var description: String {
- return "\(numberOfWheels) wheel(s)"
- }
- }
本Vehicle 类提供了其唯一的存储属性的默认值,并没有提供任何自定义初始化本身。结果,它会自动接收一个默认的初始化程序,如Default Initializers中所述。默认的初始值设定项(如果有)始终是类的指定初始值设定项,可用于创建Vehicle 带有的numberOfWheels of的新实例0 :
- let vehicle = Vehicle()
- print("Vehicle: \(vehicle.description)")
- // Vehicle: 0 wheel(s)
下一个示例定义了一个Vehicle 名为的子类Bicycle :
- class Bicycle: Vehicle {
- override init() {
- super.init()
- numberOfWheels = 2
- }
- }
该Bicycle 子类定义指定初始化一个自定义的,init() 。此指定的初始值设定项与的超类中的指定的初始值设定项匹配Bicycle ,因此Bicycle 此初始值设定项的版本标记有override 修饰符。
的init() 初始值设定项Bicycle 始于super.init() ,该会调用Bicycle 该类的超类的默认初始值设定项Vehicle 。这样可以确保numberOfWheels 继承的属性Vehicle 在Bicycle 有机会修改属性之前被初始化。调用后super.init() ,的原始值将numberOfWheels 替换为的新值2 。
如果您创建的实例Bicycle ,则可以调用其继承的description 计算属性,以查看其numberOfWheels 属性如何更新:
- let bicycle = Bicycle()
- print("Bicycle: \(bicycle.description)")
- // Bicycle: 2 wheel(s)
如果子类初始化程序在初始化过程的第2阶段不执行任何自定义操作,并且超类具有零参数指定的初始化程序,则可以super.init() 在将值分配给所有子类的所有存储属性后省略对的调用。
本示例定义的另一个子类Vehicle ,称为Hoverboard 。在其初始值设定项中,Hoverboard 该类仅设置其color 属性。super.init() |
请发表评论