swift 的init

In this Swift tutorial, we’ll be discussing an important concept, namely Swift init or Swift initialization. Initialization is what happens when we create an instance of some type.

在本Swift教程中,我们将讨论一个重要的概念,即Swift初始化或Swift初始化。 当我们创建某种类型的实例时,就会发生初始化。

Swift init() (Swift init())

Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.
初始化是准备使用的类,结构或枚举实例的过程。 此过程涉及为该实例上的每个存储属性设置一个初始值,并执行新实例准备使用之前所需的任何其他设置或初始化。

Initializers are similar to constructors in java programming. Swift being a type-safe language has placed a lot of rules for initializers. It can get tricky to implement unless you’ve got a good hold of the concept.

初始化程序类似于Java编程中的构造函数。 Swift是一种类型安全的语言,它为初始化程序设置了很多规则。 除非您很好地理解这个概念,否则可能很难实现。

Swift init()语法 (Swift init() syntax)

init() {
    // initialise the stored properties here.
}

Let’s look at a sample class below.

让我们看下面的示例类。

class A{
    
    //Compilation error. No initializer is defined.
    var a : Int
    var b : String
    var c : Int?
    let website = "JournalDev"
}

Above class won’t compile. The swift compiler complains that the stored properties aren’t initialized. Stored Properties can’t be kept in an undetermined state.

上面的类不会编译。 Swift的编译器抱怨存储的属性未初始化。 存储的属性不能保持不确定状态。

This leaves us with two possible options:

这给我们提供了两种可能的选择:

  1. Assign a default property value in the property definition itself.

    在属性定义本身中分配默认属性值。
  2. Use an initializer, init() for initializing the properties.

    使用初始化程序init()初始化属性。

Let’s look at each of the approaches one at a time.

让我们一次看看每种方法。

class A{
    
    var a : Int = 5
    var b : String = "Hello. How you're doing"
    var c : Int?
    let website = "JournalDev"
}

Here we’ve set a default value for each of the stored properties, hence Swift provides us the default initializer implicitly. All the properties and functions can be accessed using the dot operator over an instance of the class once it’s initialized.

在这里,我们为每个存储的属性设置了默认值,因此Swift隐式地为我们提供了默认初始化器。 初始化类后,可以使用点运算符在类的实例上访问所有属性和函数。

var object = A()
object.a = 10
object.c = 2

The second way is to initialize the stored properties using the init() method as shown below.

第二种方法是使用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")

Note: Swift Optional is not a stored properties. Hence, they need not be initialized.

注意Swift Optional不是存储的属性。 因此,它们不需要初始化。

Stored properties are accessed inside the init() method using self property.

使用self属性可以在init()方法内部访问存储的属性。

Note: self is used to refer to the current instance within its own instance methods (Similar to this in java).
The above initializer is the primary initializer of the class. It’s also known as designated initializer(we’ll discuss this later).

self被用来指代当前实例自身的实例方法(类似于内this在Java)。
上面的初始化器是该类的主要初始化器。 也称为指定的初始化程序 (我们将在后面讨论)。

Initializers lets us modify a constant property too.

通过初始化程序,我们也可以修改常量属性。

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")

结构的成员初始化器 (Memberwise Initializers for Structures)

Structures being value types, don’t neccessarily require an initializer defined. Structure types automatically receive a memberwise initializer unless you’ve defined custom initializer(s).

结构是值类型,不需要定义初始化程序。 除非您定义了自定义初始化程序,否则结构类型会自动接收一个成员初始化程序。

Following are the code snippets that describe the various ways to initialize a struct.

以下代码段描述了初始化结构的各种方法。

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)

Since we’ve assigned default values to the stored properties in the above snippet, we receive a default initializer without member initialization alongwith the memberwise initializer.

由于我们已在上述代码段中为存储的属性分配了默认值,因此我们将收到一个默认的初始化程序,而没有成员初始化以及逐级初始化程序。

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)

In the above case, we’ve defined our own custom initializer.

在上述情况下,我们定义了自己的自定义初始化程序。

Using Parameters without External Name
When an external name is not needed for an initializer, underscore ‘_’ is used to indicate the same as shown below.

使用不带外部名称的参数
当初始化程序不需要外部名称时,下划线“ _”用于表示相同的名称,如下所示。

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初始化程序的类型 (Types of Swift Initializers)

Initializers for classes can be broadly classified into the following types:

类的初始化程序可以大致分为以下类型:

  1. Designated Initializers: This is the primary initializer of the class. It must fully initialize all properties introduced by its class before calling any superclass initializer. A class can have more than one designated initializer. Every class must have at least one designated initializer.

    指定的初始化器 :这是该类的主要初始化器。 在调用任何超类初始化程序之前,它必须完全初始化其类引入的所有属性。 一个类可以具有多个指定的初始化程序。 每个类必须至少有一个指定的初始化程序。
  2. Convenience Initializers: These are secondary, supporting initializers for a class. They must call a designated initializer of the same class. These are optional and can be used for a custom setup. They are written in the same style, but with the convenience modifier placed before the init keyword

    便利的初始化器 :这些是辅助的,支持类的初始化器。 他们必须调用相同类的指定初始化器。 这些是可选的,可用于自定义设置。 它们以相同的样式编写,但是在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"

Convenience Initializers are useful when it comes to assigning default values to stored properties.

将默认值分配给存储的属性时,便捷初始化器很有用。

值类型的Swift初始化程序委托 (Swift Initializer Delegation For Value Types)

It’s possible to call an initializer from another one thereby avoiding code duplication. Value Types like Structures do not support inheritance. Hence the only possible way is to call initializer within the same structure. An example is given below.

可以从另一个调用初始化程序,从而避免代码重复。 像结构这样的值类型不支持继承。 因此,唯一可能的方法是在同一结构内调用初始化程序。 下面给出一个例子。

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初始化程序委托 (Swift Initializer Delegation For Reference Types)

Classes being reference types support inheritance. Thus initializers can call other initializers from superclass too thereby adding responsibilities to properly inherit and initialize all values.
Following are the primary rules defined for handling relationships between initializers.

作为引用类型的类支持继承。 因此,初始化器也可以从超类调用其他初始化器,从而增加了正确继承和初始化所有值的责任。
以下是定义用于处理初始化程序之间关系的主要规则。

  • 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.

    便利初始化程序必须最终调用指定的初始化程序。

Following illustration describes the above rules.

下图说明了上述规则。

swift init, swift initialization delegation flow

Source: Apple Docs

资料来源:Apple文件

Designated initializers must always delegate up. Convenience initializers must always delegate across.

指定的初始化程序必须始终委托。 便捷初始化程序必须始终委派。

super keyword is not possible for a convenience initializer in a subclass.

对于子类中的便捷初始化程序,无法使用super关键字。

Swift初始化程序的继承和覆盖 (Swift Initializer Inheritance and Overriding)

Subclasses in Swift do not inherit their superclass’s initializers by default unless certain conditions are met(Automatic Initializer Inheritance). This is done to prevent half-baked initialization in the subclass.
Let’s look at how designated and convenience initializers work their way through inheritance.
We’ll be defining a Vehicle base class that’ll be inherited by the relevant subclasses. We’ll use Enums as a type in the classes.

Swift中的子类默认情况下不会继承其超类的初始化器,除非满足某些条件(自动初始化器继承)。 这样做是为了防止在子类中进行半初始化。
让我们看一下指定的初始化器和便捷初始化器如何通过继承工作。
我们将定义一个Vehicle的基类,该基类将被相关的子类继承。 在类中,我们将枚举用作类型。

Our base class Vehicle is defined as shown below.

我们的基类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)

Note: The convenience initializer must call the designated initializer of the same class using self.init

注意 :便利初始化程序必须使用self.init调用同一类的指定初始化程序。

Let’s define a subclass of the above class as shown below.

让我们定义上述类的子类,如下所示。

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)
        
    }
}

Important points to note:

注意事项:

  • 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.

    只有在调用super.init之后,子类才能修改超类的继承属性。

Following code would lead to a compile-time error.

以下代码将导致编译时错误。

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)

As explained earlier, the superclass initializer isn’t inherited automatically in the subclass.
So the below initialization would fail.

如前所述,超类初始化器不会在子类中自动继承。
因此,以下初始化将失败。

var t = TwoWheeler(vehicleType: .twoWheeler) //manufacturer property isn't initialized.

To override an initializer, the subclass initializer must match with the designated initializer of the superclass. The override keyword is appended to the initializer in this case.

要覆盖初始化程序,子类初始化程序必须与超类的指定初始化程序匹配。 这种情况下, 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)
    }

The below initializer doesn’t override the one from superclass since the parameter name is different.

由于参数名称不同,下面的初始化程序不会覆盖超类中的一个。

//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)
    }

Using Convenience Initializer to override the one from superclass.

使用便捷初始化程序覆盖超类中的一个。

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

The convenience initializer has override keyword appended to it. It calls the designated initalizer of the same class.
Note: The order of the keywords convenience and override doesn’t matter.

便捷初始化程序后面附加了override关键字。 它调用相同类别的指定初始化器。
注意 :关键字convenienceoverride的顺序无关紧要。

必需的初始化器 (Required Initializers)

Writing the keyword required before the initializer indicates that each subclass must implement that initializer.
Also, the required modifier must be present at the respective subclass implementations as well.
An example of Required Initializers on the above two classes is given below.

在初始化程序之前编写关键字required表示每个子类必须实现该初始化程序。
同样, required修饰符也必须出现在相应的子类实现中。
下面给出了以上两个类的Required Initializers的示例。

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)
    }
}

Note: Adding a required modifier, indicates that the initializer would be overridden. Hence the override keyword can be ommitted in the above case.

注意 :添加必需的修饰符,表示初始化器将被覆盖。 因此,在上述情况下可以忽略override关键字。

Using a Required Initializer with Convenience
Required and convenience initializers are independent of each other and can be used together.
Let’s create another subclass of Vehicle to demonstrate the use of required and convenience modifiers together.

方便地使用所需的初始化程序
必需和便利的初始化程序彼此独立,可以一起使用。
让我们创建Vehicle的另一个子类,以一起演示requiredconvenience修饰符的用法。

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)
    }
    
}

Important things to note in the above code snippet:

上面的代码片段中要注意的重要事项:

  • Convenience initializers are secondary initializers in a class.

    便利初始化器是类中的辅助初始化器。
  • Setting a convenience initializer as required means that implementing it in the subclass is compulsory.

    根据需要设置便捷初始化器意味着必须在子类中实现它。

自动初始化程序继承 (Automatic Initializer Inheritance)

There are two circumstances under which a subclass automatically inherits the initializers from the superclass.

在两种情况下,子类会自动从超类继承初始化程序。

  • 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.

    实现超类的所有指定初始化器。 所有便利初始化程序也将自动继承。

The first rule in action is demonstrated in the snippet below:

下面的代码片段演示了第一个有效的规则:

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")

The second rule in action is demonstrated in the snippet below.

下面的代码片段演示了第二条有效规则。

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

The convenience initializer of the superclass is automatically available in the subclass in the above code.

上面代码的子类中自动提供了超类的便捷初始化器。

Swift失败的初始化程序 (Swift Failable Initializer)

We can define a failable initializer using the keyword init? on Classes, Structures or Enumerations which gets triggered when the initialization process fails.
Initialization can fail for various reasons: Invalid parameter values, absence of an external source etc.
A failable initializer creates an optional value of the type it initializes.
We’ll be returning a nil to trigger an initialization failure(Though an init doesn’t return anything).
Failable Initializers With Structures

我们可以使用关键字init?定义一个失败的初始化器init? 在初始化过程失败时触发的类,结构或枚举。
初始化可能由于各种原因而失败:无效的参数值,缺少外部源等。
失败的初始化程序会创建一个初始化类型的可选值。
我们将返回nil以触发初始化失败(尽管init不会返回任何内容)。
具有结构的失败初始化器

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
}

Failable Initializers With Enums

枚举失败的初始化程序

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")
}

Note: A failable initializer and a non-failable initializer can’t have the same parameter types and names.

注意 :失败的初始化程序和失败的初始化程序不能具有相同的参数类型和名称。

覆盖失败的初始化程序 (Overriding a Failable Initializer)

You can override a failable initializer in your subclass.
A failable initializer can be overridden with a non-failable initializer but it cannot happen vice-versa.
An example of overriding a failable with a non-failable initializer is given below.

您可以在子类中覆盖失败的初始化程序。
一个失败的初始化器可以用一个不失败的初始化器来覆盖,但是反之亦然。
下面给出了使用非失败的初始化程序覆盖失败的示例。

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)!  
    }
}

Note: Forced unwrapping is used to call a failable initializer from the superclass as part of the implementation of a subclass’s nonfailable initializer.

注意 :强制展开用于从超类调用可失败的初始化器,作为子类不可失败的初始化器实现的一部分。

This brings an end to swift initi tutorial.
References : Apple Docs

这样就结束了快速入门教程。
参考文献: Apple Docs

翻译自: https://www.journaldev.com/16889/swift-init

swift 的init

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐