The initializer

Class initializer

Classes, structs, and enumerations can all define initializers

Classes have two types of initializers

  • Designated Initializer
  • Convenience Initializer
// Init (parameters) {statements} // Easy init(parameters) {statements}Copy the code

Each class has at least one designated initializer, which is the primary initializer of the class

The default initializer is always the designated initializer for the class

class Size {
    init() {
            
    }
    
    init(age: Int) {
        
    }

    convenience init(height: Double) {
        self.init()
    }
}

var s = Size()
s = Size(height: 180)
s = Size(age: 10)
Copy the code

The class itself comes with a designated initializer

class Size {
    
}

var s = Size()
Copy the code

If there is a custom designated initializer, the default designated initializer does not exist

Classes tend to have a small number of designated initializers, and a class usually has only one

class Size {
    var width: Double = 0
    var height: Double = 0
    
    init(height: Double, width: Double) {
        self.width = width
        self.height = height
    }

    convenience init(height: Double) {
        self.init(height: height, width: 0)
    }
    
    convenience init(width: Double) {
        self.init(height: 0,width: width)
    }
}

let size = Size(height: 180, width: 70)
Copy the code

Initializers call each other

Rules for calling each other to initializers

  • The specified initializer must be called from its immediate parent
  • The convenience initializer must call another initializer from the same class
  • The convenience initializer must eventually call a designated initializer
class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
    
    convenience init() {
        self.init(age: 0)
        
        self.age = 10
    }
}

class Student: Person {
    var score: Int
    
    init(age: Int, score: Int) {
        
        self.score = score
        super.init(age: age)
        
        self.age = 30
    }
    
    convenience init(score: Int) {
        self.init(age: 0, score: score)
        
        self.score = 100
    }
}
Copy the code

This set of rules ensures that instances can be fully initialized using any initializer

Two-stage initialization and security check

Swift takes great pains in coding security, setting up a two-stage initialization and security check to ensure the security of the initialization process

Two-stage initialization

Phase 1: Initialize all storage properties

  • The outer call specifies the \ convenience initializer
  • Memory allocated to an instance, but not initialized
  • Specifying an initializer ensures that the storage properties defined by the current class are initialized
  • Specifies that the initializer calls the parent class’s initializer, calling it up and up, forming a chain of initializers

Stage 2: Set new storage property values

  • From the top initializer down, each designated initializer in the chain has the opportunity to further customize the instance
  • The initializer is now availableself(Access, modify its properties, call its instance methods, etc.)
  • Ultimately, any convenient initializer in the chain has the opportunity to customize instances and use themself

The security check

  • Specifying initializers must ensure that all storage properties defined by the parent class are initialized before the parent class initializer is called
  • Specifies that the initializer must call the parent initializer before it can set new values for inherited properties
  • The convenience initializer must first call other initializers in the class before setting new values for any properties
  • The initializer cannot call any instance methods, read the values of any instance properties, or reference any instance properties until the first phase of initialization is completeself
  • The instance is not fully legal until the end of the first phase

rewrite

Override (even the convenient initializer for a subclass’s implementation) must be added when overriding the specified initializer of the parent class.

Specifies that initializers can only be called vertically and can be called by subclasses

class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
    
    convenience init() {
        self.init(age: 0)
        
        self.age = 10
    }
}

class Student: Person {
    var score: Int
    
    override init(age: Int) {
        self.score = 0
        super.init(age: age)
    }
}
Copy the code
class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
    
    convenience init() {
        self.init(age: 0)
        
        self.age = 10
    }
}

class Student: Person {
    var score: Int
    
    init(age: Int, score: Int) {
           
        self.score = score
        super.init(age: age)
    }
    
    override convenience init(age: Int) {
        self.init(age: age, score: 0)
    }
}
Copy the code

If a subclass writes an initializer that matches the parent class’s convenient initializer, override is not required

class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
    
    convenience init() {
        self.init(age: 0)
    }
}

class Student: Person {
    var score: Int
    
    init(age: Int, score: Int) {
           
        self.score = score
        super.init(age: age)
    }
    
    convenience init() {
        self.init(age: 0, score: 0)
    }
}
Copy the code

Because a parent’s convenience initializer is never called directly by a child class, technically, a child class cannot override the parent’s convenience initializer

Convenient initializers can only be called horizontally, not by subclasses

Subclasses do not have the right to change their parent’s convenient initializer, so they cannot be overridden

class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
    
    convenience init() {
        self.init(age: 0)
    }
}

class Student: Person {
    var score: Int
    
    init(age: Int, score: Int) {
           
        self.score = score
        super.init(age: age)
    }
    
    init() {
        self.score = 0
        super.init(age: 0)
    }
}
Copy the code

Automatically inherit

1. If a subclass does not define any specified initializers, it automatically inherits all specified initializers from its parent class

class Person {
    var age: Int
    
    init(age: Int) {
        self.age = age
    }
}

class Student: Person {
    
}

var s = Student(age: 20)
Copy the code
class Person {
    var age: Int
    
    init(age: Int) {
        self.age = age
    }
}

class Student: Person {
    
    convenience init(name: String) {
        self.init(age: 0)
    }
}

var s = Student(name: "ray")
s = Student(age: 20)
Copy the code

2. If the subclass provides all implementations of the specified initializers of the parent class (either inherited from the previous method or re-implemented)

class Person {
    var age: Int
    
    init(age: Int) {
        self.age = age
    }
    
    convenience init(sex: Int) {
        self.init(age: 0)
    }
}

class Student: Person {
    
    override init(age: Int) {
        super.init(age: 20)
    }
}

var s = Student(age: 30)
Copy the code
class Person {
    var age: Int
    
    init(age: Int) {
        self.age = age
    }
    
    convenience init(sex: Int) {
        self.init(age: 0)
    }
}

class Student: Person {
    
    init(num: Int) {
        super.init(age: 0)
    }
    
    override convenience init(age: Int) {
        self.init(num: 200)
    }
}

var s = Student(age: 30)
Copy the code

3. If a subclass has a custom designated initializer, the parent class’s designated initializer will not be inherited

Subclasses automatically inherit all of their parent class’s convenience initializers

4. These rules apply even if subclasses add more convenient initializers

class Person {
    var age: Int
    
    init(age: Int) {
        self.age = age
    }
    
    convenience init(sex: Int) {
        self.init(age: 0)
    }
}

class Student: Person {
    
    convenience init(isBoy: Bool) {
        self.init(age: 20)
    }
    
    convenience init(num: Int) {
        self.init(age: 20)
    }
}

var s = Student(age: 30)
s = Student(sex: 24)
s = Student(isBoy: true)
s = Student(num: 6)
Copy the code

5. Subclasses re-initialize the parent class’s designated initializer in the form of a convenient initializer, or as part of meeting rule 2

class Person {
    var age: Int
    
    init(age: Int) {
        self.age = age
    }
    
    convenience init(sex: Int) {
        self.init(age: 0)
    }
}

class Student: Person {
    
    convenience init(sex: Int) {
        self.init(age: 20)
    }
}

var s = Student(age: 30)
s = Student(sex: 24)
Copy the code

required

Specify the initializer with the required modifier, indicating that all of its subclasses must implement the initializer (by inheritance or overwriting)

class Person {
    var age: Int
    
    init(age: Int) {
        self.age = age
    }
    
    required init() {
        self.age = 0
    }
}

class Student: Person {
    
    
}

var s = Student(age: 30)
Copy the code

If a subclass overrides the required initializer, it must also add required instead of Override

class Person {
    var age: Int
    
    init(age: Int) {
        self.age = age
    }
    
    required init() {
        self.age = 0
    }
}

class Student: Person {
    
    init(num: Int) {
        super.init(age: 0)
    }
    
    required init() {
        super.init()
    }
}

var s = Student(num: 30)
s = Student()
Copy the code

Attribute viewer

Assignment of a parent class’s property in its own initializer does not trigger the property observer, but assignment in a subclass’s initializer does

class Person {
    var age: Int {
        willSet {
            print("willSet", newValue)
        }
        
        didSet {
            print("didSet", oldValue, age)
        }
    }
    
    init() {
        self.age = 0
    }
}

class Student: Person {
    
    override init() {
        super.init()
        
        age = 1
    }
}

var s = Student()
Copy the code

Initializers can fail

Classes, structs, enumerations can all use init, right? Defines failable initializers

class Person { var name: String init? (name: String) { if name.isEmpty { return nil } self.name = name } } let p = Person(name: "Jack") print(p)Copy the code

The following ones also use failable initializers

var num = Int("123")

enum Answer: Int {
    case wrong, right
}

var an = Answer(rawValue: 1)
Copy the code

It is not allowed to define failed initializers and non-failed initializers with the same parameter label, parameter number, and parameter type at the same time

You can use the init! Defines a failable initializer for implicit unpacking

class Person { var name: String init! (name: String) { if name.isEmpty { return nil } self.name = name } } let p = Person(name: "Jack") print(p)Copy the code

Failable initializers can call non-failable initializers, which require unpacking

class Person { var name: String convenience init? (name: String) { self.init() if name.isEmpty { return nil } self.name = name } init() { self.name = "" } }Copy the code
class Person { var name: String init? (name: String) {if name.isempty {return nil} self.name = name} Convenience init() {// Risk of forcing to unpack self.init(name: "")! self.name = "" } }Copy the code

If the initializer calls a failable initializer and the initialization fails, the entire initialization process fails and the subsequent code stops execution

class Person { var name: String init? (name: String) {if name.isempty {return nil} self.name = name} convenience init?() {// If this step returns nil, Self.init (name: "")! self.name = "" } } let p = Person() print(p)Copy the code

It is possible to rewrite a failable initializer with a non-failable initializer, but not the other way around

class Person {
    var name: String
    
    init?(name: String) {
        
        if name.isEmpty {
            return nil
        }
        
        self.name = name
    }
}

class Student: Person {

    override init(name: String) {
        super.init(name: name)!
    }
}
Copy the code

Deinitializer (deinit)

Deinit is called a de-initializer, similar to the C++ destructor, the dealloc method in OC

When the instance object of the class is freed, the deinit method of the instance object is called

Class Person {var name: String init(name: String) {self.name = name} deinit {print("Person object destroyed ")}}Copy the code

Deinit of a parent class can be inherited by subclasses

When the subclass’s deinit implementation is done, it calls the parent class’s deinit

class Person { var name: String init(name: String) {self.name = name} deinit {print("Person object destroyed ")}} class Student: Person {deinit {print("Student object destroyed ")}} func test() {let stu = Student(name: "Jack")} test() // prints // Student object destroyed // Person object destroyedCopy the code

Deinit takes no arguments, can’t write curly braces, can’t call itself

Optional Chaining

Take a look at the sample code below

class Person {
    var name: String = ""
    var dog: Dog = Dog()
    var car: Car? = Car()
    
    func age() -> Int { 18 }
    
    func eat() {
        print("Person eat")
    }
    
    subscript(index: Int) -> Int { index }
}
Copy the code

If optional is nil, method, subscript, property calls fail and result is nil

var person: Person? = nil var age = person? .age() var name = person? .name var index = person?[6] print(age, name, index) // nil, nil, nilCopy the code
// If person is nil, getName func getName() -> String {"jack"} var person: person? = nil person? .name = getName()Copy the code

If the optional is not nil, the method, subscript, and property are called successfully, and the result is wrapped as optional

var person: Person? = Person() var age = person? .age() var name = person? .name var index = person?[6] print(age, name, index) // Optional(18) Optional("") Optional(6)Copy the code

If the result is already optional, it will not be repackaged

print(person? .car) // Optional(test_enum.Car)Copy the code

Optional bindings can be used to determine the success of optional method calls

let result: ()? = person? .eat() if let _ = result {print(" call successful ")} else {print(" call failed ")}Copy the code
if let age = person? . The age () {print (" the call is successful, the age)} else {print (" failed ")}Copy the code

Methods that do not return a value return the tuple type by default

More than one? Can be linked together to form optional chains

var dog = person? .dog var weight = person? .dog.weight var price = person? .car? .priceCopy the code

As long as one node in an optional chain is optional, no matter how many layers it goes through, the end result will be wrapped as optional

print(dog, weight, price) // Optional(test_enum.Dog) Optional(0) Optional(0)
Copy the code

If any node in the chain is nil, then the whole chain will fail the call

Take a look at the sample code below

var num1: Int? = 5
num1? = 10
print(num1)

var num2: Int? = nil
num2? = 10
print(num2)
Copy the code

I’m going to add what? It’s to see if the variable is nil, and if it’s nil, then it doesn’t do the assignment, which is essentially an optional chain

var dict: [String : (Int, Int) -> Int] = [ "sum" : (+), "difference" : (-) ] var value = dict["sum"]? (10, 20) print(value)Copy the code

If you get the key from the dictionary, you get an optional type, and since one of the nodes in the optional chain is optional, then the final result is optional, and the final value is also Int, right?