快速的初始()
在这个Swift教程中,我们将讨论一个重要的概念,即Swift的初始化或Swift初始化。初始化是在我们创建某个类型的实例时发生的过程。
快速初始化()
初始化是为了使用而准备一个类、结构体或枚举实例的过程。这个过程包括为该实例上的每个存储属性设置初始值,并执行任何其他设置或初始化操作,在新实例准备就绪之前需要完成。
初始化器和Java编程中的构造函数类似。作为一种类型安全的语言,Swift对初始化器制定了许多规则。如果你对这个概念没有很好的掌握,实现起来可能会有些棘手。
Swift的init()语法
init() {
// initialise the stored properties here.
}
让我们来看下面的一个样本课程。 de .)
class A{
//Compilation error. No initializer is defined.
var a : Int
var b : String
var c : Int?
let website = "JournalDev"
}
上述的类无法编译。Swift编译器指出存储属性没有被初始化。存储属性不能保持在不确定的状态。这给我们留下了两个可能的选择:
-
- 在属性定义中分配默认属性值。
- 使用初始化器init()来初始化属性。
我们逐个来看每种方法。
class A{
var a : Int = 5
var b : String = "Hello. How you're doing"
var c : Int?
let website = "JournalDev"
}
在这里,我们为每个存储属性设置了默认值,因此Swift隐式地为我们提供了默认初始化程序。一旦初始化完成,可以使用点运算符在类的实例上访问所有属性和函数。
var object = A()
object.a = 10
object.c = 2
第二种方法是使用以下所示的init()方法来初始化存储属性。
class A{
var a : Int
var b : String
var c : Int?
let website = "JournalDev"
init(a: Int, b: String) {
self.a = a
self.b = b
}
}
var object = A(a: 5, b: "Hello World")
注意:Swift Optional 不是存储属性。因此,它们不需要初始化。存储属性可以在 init() 方法中使用 self 属性进行访问。注意:self 用于在其自身实例方法内引用当前实例(类似于 Java 中的 this)。上述初始化程序是该类的主要初始化程序。它也被称为指定的初始化程序(我们稍后会讨论这个)。初始化程序还允许我们修改常量属性。
class A{
var a : Int
var b : String
var c : Int?
let website : String
init(a: Int, b: String, website: String) {
self.a = a
self.b = b
self.website = website
}
}
var object = A(a: 5,b: "Hello World", website: "JournalDev")
结构体的成员初始化器
结构体是值类型,不一定需要定义初始化器。结构体类型会自动获得成员逐一初始化器,除非你定义了自定义的初始化器。以下是描述结构体初始化的代码片段。
struct Rect{
var length : Int
var breadth : Int
}
var r = Rect(length: 5, breadth: 10)
struct Rect{
var length : Int = 5
var breadth : Int = 10
}
var r = Rect()
var r1 = Rect(length: 10, breadth: 5)
由于我们在上面的代码片段中为存储属性分配了默认值,所以我们同时获得了一个不带成员初始化的默认初始化器以及成员逐一初始化器。
struct Rect{
var length : Int
var breadth : Int
init(length: Int, breadth: Int) {
self.length = length + 10
self.breadth = breadth + 10
}
}
var r = Rect(length: 10, breadth: 5)
在上述情况中,我们已经定义了我们自己的自定义初始化函数。当初始化函数不需要外部名称时,下划线’_’用于表示相同的含义,如下所示。
class A{
var a : Int
var b : String
var c : Int?
let website = "JournalDev"
init(_ a: Int, _ b: String) {
self.a = a
self.b = b
}
}
var object = A(5,"Hello World")
struct Rect{
var length : Int
var breadth : Int
init(_ length: Int, _ breadth: Int) {
self.length = length + 10
self.breadth = breadth + 10
}
}
var r = Rect(10, 10)
Swift初始化器的类型
类的初始化器可以大致分为以下几种类型:
-
- 指定初始化器:这是类的主要初始化器。在调用任何超类初始化器之前,它必须完全初始化其类引入的所有属性。一个类可以有多个指定初始化器。每个类必须至少有一个指定初始化器。
- 便利初始化器:这些是类的次要、支持性的初始化器。它们必须调用同一类的指定初始化器。这些是可选的,并且可以用于自定义设置。它们以相同的格式编写,但是在init关键字之前放置了convenience修饰符。
class Student{
var name : String
var degree : String
init(name : String, degree: String) {
self.name = name
self.degree = degree
}
convenience init()
{
self.init(name: "Unnamed", degree: "Computer Science")
}
}
var student = Student()
student.degree // "Computer Science"
student.name // "Unnamed"
当涉及指定存储属性的默认值时,便利构造器非常有用。
值类型的Swift初始化委托技术
在结构中,可以通过调用另一个初始化程序来避免代码重复,从而实现。值类型如结构不支持继承。因此,唯一可能的方法是在相同的结构中调用初始化程序。以下是一个例子。
struct Rect{
var length : Int
var breadth : Int
init(_ length: Int, _ breadth: Int) {
self.length = length
self.breadth = breadth
}
init(_ length: Int)
{
self.init(length, length)
}
}
var r = Rect(10, 5)
var r1 = Rect(15) //initialises the length and breadth to 15
对于引用类型的Swift初始化器委托方式
类作为引用类型,支持继承。因此,初始化器可以调用超类的其他初始化器,从而增加了正确继承和初始化所有值的责任。以下是处理初始化器之间关系的主要规则。
- A designated initializer must call a designated initializer from its immediate superclass.
- A convenience initializer must call another initializer from the same class.
- A convenience initializer must ultimately call a designated initializer.
以下插图描述了上述规则。
指定的初始化器必须始终向上委派。便利初始化器必须始终横向委派。在子类中的便利初始化器不能使用super关键字。
快速初始化器的继承和重写
在Swift中,子类默认情况下不会继承父类的初始化器,除非满足一定条件(自动初始化器继承)。这是为了防止子类中的不完整初始化。让我们来看看指定初始化器和便利初始化器是如何通过继承起作用的。我们将定义一个作为相关子类所继承的基类Vehicle。我们将在类中使用枚举作为类型。如下所示,我们的基类Vehicle被定义如下。
enum VehicleType : String {
case twoWheeler = "TwoWheeler"
case fourWheeler = "FourWheeler"
}
class Vehicle{
var vehicleType : VehicleType
init(vehicleType: VehicleType) {
self.vehicleType = vehicleType
print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
}
convenience init()
{
self.init(vehicleType: .fourWheeler)
}
}
var v = Vehicle(vehicleType: .twoWheeler)
注意:方便初始化器必须使用 self.init 调用同类的指定初始化器。让我们定义一个上述类的子类如下所示。
enum TwoWheelerType : String
{
case scooty = "Scooty"
case bike = "Bike"
}
class TwoWheeler : Vehicle{
var twoWheelerType : TwoWheelerType
var manufacturer : String
init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
self.twoWheelerType = twoWheelerType
self.manufacturer = manufacturer
print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
super.init(vehicleType: vType)
}
}
需要注意的重要点:
- The designated initializer of the subclass must initialize its own properties before calling the designated initializer of the superclass.
- A subclass can modify inherited properties of the superclass only after the super.init is called.
以下代码将导致编译时错误。
class TwoWheeler : Vehicle{
var twoWheelerType : TwoWheelerType
var manufacturer : String
init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
self.twoWheelerType = twoWheelerType
self.manufacturer = manufacturer
self.vehicleType = vType //Won't compile
super.init(vehicleType: vType)
//self.vehicleType = .fourWheeler //This would work.
}
}
var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)
如前所述,子类不会自动继承超类的初始化程序。因此,下面的初始化将失败。
var t = TwoWheeler(vehicleType: .twoWheeler) //manufacturer property isn't initialized.
在子类中覆盖一个初始化函数时,子类的初始化函数必须与父类的指定初始化函数匹配。在这种情况下,需要在初始化函数后添加override关键字。
class TwoWheeler : Vehicle{
var twoWheelerType : TwoWheelerType
var manufacturer : String
init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
self.twoWheelerType = twoWheelerType
self.manufacturer = manufacturer
print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
super.init(vehicleType: vType)
}
override init(vehicleType: VehicleType)
{
print("Class TwoWheeler. Overriden Initializer. \(vehicleType.rawValue)")
self.twoWheelerType = .bike
self.manufacturer = "Not defined"
super.init(vehicleType: vehicleType)
}
由于参数名称不同,下面的初始化程序不会覆盖超类的初始化程序。
//This would give a compile-time error since the parameter v doesn't match with the superclass.
override init(v: VehicleType)
{
self.twoWheelerType = .bike
self.manufacturer = "Not defined"
super.init(vehicleType: v)
}
使用方便构造器覆写超类中的构造器。
class TwoWheeler : Vehicle{
var twoWheelerType : TwoWheelerType
var manufacturer : String
init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
self.twoWheelerType = twoWheelerType
self.manufacturer = manufacturer
print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
super.init(vehicleType: vType)
}
override convenience init(vehicleType: VehicleType) {
self.init(twoWheelerType: .bike, manufacturer: "Not Defined", vType: .twoWheeler)
self.vehicleType = vehicleType
}
}
var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)
t = TwoWheeler(vehicleType: .twoWheeler)
//Output
Following gets printed on the console:
Class TwoWheeler. Scooty manufacturer is Hero Honda
Class Vehicle. vehicleType is TwoWheeler
Class TwoWheeler. Bike manufacturer is Not Defined
Class Vehicle. vehicleType is TwoWheeler
方便初始化器加上了覆盖(override)关键字,它调用了同一类的指定初始化器。注意:方便(convenience)和覆盖(override)关键字的顺序无关紧要。
必需的初始化器
在初始化程序之前编写所需的关键字表示每个子类都必须实现该初始化程序。此外,所需的修饰符也必须在相应的子类实现中存在。以下是上述两个类中所需的初始化程序的示例。
class Vehicle{
var vehicleType : VehicleType
required init(vehicleType: VehicleType) {
self.vehicleType = vehicleType
print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
}
convenience init()
{
self.init(vehicleType: .fourWheeler)
}
}
class TwoWheeler : Vehicle{
var twoWheelerType : TwoWheelerType
var manufacturer : String
init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
self.twoWheelerType = twoWheelerType
self.manufacturer = manufacturer
print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
super.init(vehicleType: vType)
}
required init(vehicleType: VehicleType) {
self.manufacturer = "Not Defined"
self.twoWheelerType = .bike
super.init(vehicleType: vehicleType)
}
}
注意:添加一个required修饰符,表示初始化器将被覆盖。因此,在上述情况下可以省略”override”关键字。在使用required初始化器时,required和convenience初始化器是相互独立的,可以一起使用。让我们创建Vehicle的另一个子类,来演示required和convenience修饰符一起使用的情况。
enum FourWheelerType : String
{
case car = "Car"
case bus = "Bus"
case truck = "Truck"
}
class FourWheeler : Vehicle
{
var fourWheelerType : FourWheelerType
var name : String
init(fourWheelerType : FourWheelerType, name: String, vehicleType: VehicleType) {
self.fourWheelerType = fourWheelerType
self.name = name
print("Class FourWheeler. \(self.fourWheelerType.rawValue) Model is \(self.name)")
super.init(vehicleType: vehicleType)
self.vehicleType = vehicleType
}
required convenience init(vehicleType: VehicleType) {
self.init(fourWheelerType: .bus, name: "Mercedes", vehicleType: vehicleType)
}
}
class Car : FourWheeler{
var model : String
init(model: String) {
self.model = model
print("Class Car. Model is \(self.model)")
super.init(fourWheelerType: .car, name: self.model, vehicleType: .fourWheeler)
}
required init(vehicleType: VehicleType)
{
self.model = "Not defined"
print("Class Car. Model is \(self.model)")
super.init(fourWheelerType: .car, name: self.model, vehicleType: vehicleType)
}
}
上述代码片段中需要注意的重要事项:
- Convenience initializers are secondary initializers in a class.
- Setting a convenience initializer as required means that implementing it in the subclass is compulsory.
自动初始化继承
子类会自动继承超类的初始化方法的情况有两种。
- Don’t define any designated initializers in your subclass.
- Implement all the designated initializers of the superclass. All the convenience initializers would be automatically inherited too.
行动中的第一条规则在下面的片段中得以展示:
class Name {
var name: String
init(n: String) {
self.name = n
}
}
class Tutorial: Name {
var tutorial : String? = "Swift Initialization"
}
var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "JournalDev")
在下面的片段中展示了行动中的第二条规则。
class Name {
var name: String
init(n: String) {
self.name = n
}
convenience init()
{
self.init(n: "No name assigned")
}
}
class Tutorial: Name {
var tutorial : String? = "Swift Tutorial"
override init(n : String) {
super.init(n: n)
}
}
var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "JournalDev")
var childObject2 = Tutorial()
print(childObject2.name) //prints "No name assigned
在上述代码中,子类自动拥有父类的便捷初始化器。
快速可失败的初始化器
我们可以在类、结构体或枚举上使用关键字init?定义一个可失败的初始化器,当初始化过程失败时会被触发。初始化可能因为各种原因而失败:无效的参数值,外部资源的缺失等。可失败的初始化器会创建一个所初始化类型的可选值。我们会返回nil来触发初始化失败(虽然初始化器本身不返回任何内容)。具有结构体的可失败初始化器。
struct SName {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
var name = SName(name: "JournalDev")
if name != nil {
print("init success") //this gets displayed
}
else{
print("init failed")
}
name = SName(name: "")
if name != nil {
print("init success")
}
else{
print("init failed") //this gets displayed
}
带有枚举的可失败初始化程序
enum CharacterExists {
case A, B
init?(symbol: Character) {
switch symbol {
case "A":
self = .A
case "B":
self = .B
default:
return nil
}
}
}
let ch = CharacterExists(symbol: "C")
if ch != nil {
print("Init failed. Character doesn't exist")
}
class CName {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
var name = CName(name: "")
if name != nil {
print("init success")
}
else{
print("init failed")
}
注意:可失败的初始器和不可失败的初始器不能有相同的参数类型和名称。
覆写一个可失败的初始化函数
你可以在子类中重写一个可失败的初始化器。可失败的初始化器可以被一个非可失败的初始化器重写,但反之则不行。以下是用非可失败的初始化器重写可失败的初始化器的示例。
class CName {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
var name = CName(name: "")
class SubName : CName{
var age : Int
override init(name: String)
{
self.age = 23
super.init(name: name)!
}
}
注意:在子类的非可失败初始化器的实现过程中,强制解包被用于调用超类中的可失败初始化器。这标志着 Swift 初始化教程的结束。参考资料:Apple官方文档。