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 available
self
(Access, modify its properties, call its instance methods, etc.) - Ultimately, any convenient initializer in the chain has the opportunity to customize instances and use them
self
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 complete
self
- 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?