[Swift] Type component ~ Initializer ~

Initializer

The initializer initializes an instance of the type.

** All properties must have their values ​​assigned by the time the instantiation is complete. ** **

Therefore, a property that does not have an initial value when declaring the property is Must be initialized in the initializer.

Lazy stored properties aren't generated until you access them, It is a little different from the state without the initial value.

Definition method

The initializer is declared with the init keyword. As the definition contents, ** arguments and processing related to initialization ** are defined.

The initializer argument syntax is the same as the function argument.


init(argument) {
Initialization process
}

I created a structure Sample as an example.

The part of init (a: String) {} is the initializer. It receives one argument of type String.

self a in self.a = a points to property a. The a on the right side points to the argument name a.

In this process, the argument value hello was stored in the property a.

Property b is a getter, so I am receiving the value in sample.b.


struct Sample {
    let a: String
    var b: String {
        return "a = \(a)"
    }
    
    init(a: String) {
        self.a = a
    }
}

let sample = Sample(a: "hello")
print(sample.b)

Execution result
a = hello

Failable initializer

The initializer is essentially responsible for initializing all properties with the correct type of value, Depending on the argument of the initializer, the property may not be initialized in some cases.

** Initializers that may fail to initialize It can be represented as a failable initializer and returns the result as an Optional \ type. ** **

Is the failable initializer an init keyword? And write as init? (Argument).

If initialization fails, instantiation will not be performed, so It is possible to leave the property uninitialized.

I wrote an example of a failable initializer.

The flow is written in order below.


struct Student {
    let name: String
    let number: Int
    
    init?(info: [String: Any]) {
        guard let name = info["name"] as? String, let number = info["number"] as? Int else {
            return nil
        }
        
        self.name = name
        self.number = number
    }
}


let students: [[String: Any]] = [
    ["name": "Aihara", "number": 1],
    ["name": "Inoue", "number": 2],
    ["name": "Tanaka"],
    ["name": "Watanabe", "number": 40]
]

for student in students {
    if let item = Student(info: student) {
        print(item)
    } else {
        print("Error: Student information\(student)Could not generate a Student from.")
    }
}

Execution result
Student(name: "Aihara", number: 1)
Student(name: "Inoue", number: 2)
Error: Student information["name": "Tanaka"]Could not generate a Student from.
Student(name: "Watanabe", number: 40)

** Definition of 2D array ** let students: [[String: Any]] = [・ ・ ・] I have defined a two-dimensional array that contains a [String: Any] type dictionary.

** for statement ** for student in students {・ ・ ・} Store the [String: Any] type value of the contents of the two-dimensional array defined above in student and turn the for statement.

** if statement in for statement ** if let item = Student (info: student) {・ ・ ・} The value contained in student is specified as an argument, and the structure Student is instantiated. If the value is not nil, put the value in item, if it is nil, move to the else clause.

** Initializer ** init? (info: [String: Any]) {・ ・ ・} Initialize with the arguments passed when instantiated.

guard let name = info["name"] as? String The value given as an argument can be accessed from the argument name info.

As a process, since the key is set to name with info ["name "], The corresponding value is fetched as as? String, that is, String type.

The same flow applies to let number ・ ・ ・.

If both values ​​are present, self.name = name and self.number = number are executed and the values ​​are entered respectively.

But if either value doesn't exist, it goes into the guard-let statement and return nil returns nil.

It becomes the flow of processing. It's a little confusing, but did you understand ...?

I'm sorry if the explanation is not good.

Initialization check

Property initialization is checked by the compiler.

If even one property has not been initialized ** You will get a compile error because the types will be inconsistent. ** **

The following code is an example of a compilation error.

The reason for the error is ** Property name value is not set when instantiating Sample type An error will occur because not all the values ​​that the instance of Samole type should have are available. ** **


struct Sample {
    var name: Int
    var message: String {
        return "hello \(name)"
    }
    
    init(name: String) {
        //Compile error
    }
}

Error details: Return from initializer without initializing all stored properties Japanese translation: Return from initializer without initializing all saved properties

If you perform initialization processing for all properties in the initializer, You can maintain type integrity and eliminate compilation errors.


struct Sample {
    var name: String
    var message: String {
        return "hello \(name)"
    }
    
    init(name: String) {
        self.name = ""
    }
}

Also, even if you think you are initializing the property, The condition that ends instantiation without initializing Unfortunately, even if there is even one **, it cannot be compiled.


struct Sample {
    var name: String
    var message: String {
        return "hello \(name)"
    }
    
    init(dictionary: [String: String]) {
        if let name = dictionary["name"] {
            self.name = name
        }
        //Compile error
    }
}

With if let name = dictionary ["name "], If the value exists, the value is entered in the property name.

However, since nothing is written when the value does not exist, The property name will exit without a value.

As a workaround, ** describe the initialization process in the else clause **, ** make it a failable initializer , There are select fingers such as ** Fill properties with default values ​​.


//Describe the process in the else clause
struct ElseSample {
    var name: String
    var message: String {
        return "hello \(name)"
    }
    
    init(dictionary: [String: String]) {
        if let name = dictionary["name"] {
            self.name = name
        } else {
            self.name = "none"
        }
    }
}

// init?()Can fail using initializer
struct InitSample {
    var name: String
    var message: String {
        return "Hello \(name)"
    }
    
    init?(dictionary: [String: String]) {
        guard let name = dictionary["name"] else {
            return nil
        }
        self.name = name
    }
}

//Put a value in the property with the default value
struct DefaultSample {
    var name: String
    var message: String {
        return "Hello \(name)"
    }
    
    init(dictionary: [String: String]) {
        name = dictionary["name"] ?? "none"
    }
}

The above is the explanation of the initializer.

Since the initializer is an important component in creating a structure, Please remember it and use it in various situations!

We also have articles about other components, so please have a look!

[Swift] Type Components-Type Basics-[Swift] type component ~ Property Part 1 ~ -[Swift] type component ~ method ~ -[Swift] type component ~ subscript ~ -[Swift] type component ~ extension ~ -[Swift] Type component ~ Type nesting ~

Thank you for watching until the end.

Recommended Posts

[Swift] Type component ~ Initializer ~
[Swift] Type component ~ Subscript ~
[Swift] Type component ~ Method ~
[Swift] Type component ~ Property Part 2 ~
[Swift] Type type ~ Enumeration type ~
[Swift] Type type ~ Structure ~
[Swift] Type components ~ Type nesting ~
[Swift] Type design guidelines
[Swift] Shared enumeration type
[Swift] Type type-Class sequel-
[Swift] Summary about Bool type
[Swift] Type type-Class first part-
Great Swift pointer type commentary
[Swift] Converts a UInt64 type integer to [UInt8]