Processus de construction rapide


Le processus de construction est le processus de préparation à l'utilisation d'une instance d'un type classe, structure ou énumération. Ce processus comprend la définition des valeurs initiales pour chaque propriété de l'instance et l'exécution des tâches de préparation et d'initialisation nécessaires.

Le constructeur Swift utilise la méthode init().

Contrairement aux constructeurs en Objective-C, les constructeurs de Swift n'ont pas besoin de renvoyer de valeur. Leur tâche principale est de s'assurer que les nouvelles instances sont correctement initialisées avant leur première utilisation.

Les instances de classe peuvent également effectuer le travail de nettoyage de la mémoire avant que l'instance de classe ne soit libérée en définissant un désinitialiseur.



Affectation initiale des attributs stockés

Lorsque des instances de classes et de structures sont créées, des valeurs initiales appropriées doivent être définies pour tous les attributs stockés .

Lorsque les propriétés stockées sont attribuées dans le constructeur, leurs valeurs sont définies directement et aucun observateur de propriété n'est déclenché.

Le processus d'attribution des propriétés de stockage dans le constructeur :

  • Créer la valeur initiale.

  • Spécifie la valeur de propriété par défaut dans la définition de la propriété.

  • Initialisez l'instance et appelez la méthode init().


Constructeur

Le constructeur est appelé lors de la création d'une nouvelle instance d'un type spécifique. Sa forme la plus simple est similaire à une méthode d'instance sans aucun paramètre, nommée d'après le mot-clé init.

Syntaxe

init()
{
    // 实例化后执行的代码
}

Instance

La structure suivante définit un constructeur init sans paramètres, et initialise les valeurs des attributs stockés length et width à 6. et 12 :

struct rectangle {
    var length: Double
    var breadth: Double
    init() {
        length = 6
        breadth = 12
    }
}
var area = rectangle()
print("矩形面积为 \(area.length*area.breadth)")

Le résultat de l'exécution du programme ci-dessus est :

矩形面积为 72.0

Valeur de l'attribut par défaut

Nous pouvons définir la valeur initiale de l'attribut stocké dans le constructeur de la même manière ; , vous pouvez définir une valeur par défaut pour une propriété lorsqu'elle est déclarée.

L'utilisation des valeurs par défaut peut rendre votre constructeur plus simple et plus clair, et le type de la propriété peut être automatiquement déduit de la valeur par défaut.

Dans l'exemple suivant, nous définissons la valeur par défaut de l'attribut lorsqu'il est déclaré :

struct rectangle {
	// 设置默认值
    var length = 6
    var breadth = 12
}
var area = rectangle()
print("矩形的面积为 \(area.length*area.breadth)")

Le résultat de sortie de l'exécution du programme ci-dessus est :

矩形面积为 72

Paramètres de construction

Vous pouvez fournir des paramètres de construction lors de la définition du constructeur init(), comme indiqué ci-dessous :

struct Rectangle {
    var length: Double
    var breadth: Double
    var area: Double
    
    init(fromLength length: Double, fromBreadth breadth: Double) {
        self.length = length
        self.breadth = breadth
        area = length * breadth
    }
    
    init(fromLeng leng: Double, fromBread bread: Double) {
        self.length = leng
        self.breadth = bread
        area = leng * bread
    }
}

let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("面积为: \(ar.area)")

let are = Rectangle(fromLeng: 36, fromBread: 12)
print("面积为: \(are.area)")

Le résultat de l'exécution du programme ci-dessus est :

面积为: 72.0
面积为: 432.0

Paramètres internes et externes Le nom

est le même que celui des paramètres de fonction et de méthode. Il existe également un nom de paramètre utilisé à l'intérieur du constructeur et un nom de paramètre externe utilisé lors de l'appel du constructeur.

Cependant, les constructeurs n'ont pas de nom distinctif avant les parenthèses comme les fonctions et les méthodes. Par conséquent, lors de l'appel d'un constructeur, le constructeur à appeler est principalement déterminé par le nom et le type du paramètre dans le constructeur.

Si vous ne fournissez pas de noms externes pour les paramètres lors de la définition d'un constructeur, Swift générera automatiquement un nom externe pour chaque paramètre du constructeur qui est le même que le nom interne.

struct Color {
    let red, green, blue: Double
    init(red: Double, green: Double, blue: Double) {
        self.red   = red
        self.green = green
        self.blue  = blue
    }
    init(white: Double) {
        red   = white
        green = white
        blue  = white
    }
}

// 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)

print("red 值为: \(magenta.red)")
print("green 值为: \(magenta.green)")
print("blue 值为: \(magenta.blue)")

// 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let halfGray = Color(white: 0.5)
print("red 值为: \(halfGray.red)")
print("green 值为: \(halfGray.green)")
print("blue 值为: \(halfGray.blue)")

Le résultat de l'exécution du programme ci-dessus est :

red 值为: 1.0
green 值为: 0.0
blue 值为: 1.0
red 值为: 0.5
green 值为: 0.5
blue 值为: 0.5

Aucun paramètre de nom externe

Si vous ne souhaitez pas fournir de nom externe pour un paramètre du constructeur, vous pouvez utiliser un trait de soulignement _ pour afficher le nom externe le décrivant.

struct Rectangle {
    var length: Double
    
    init(frombreadth breadth: Double) {
        length = breadth * 10
    }
    
    init(frombre bre: Double) {
        length = bre * 30
    }
    //不提供外部名字
    init(_ area: Double) {
        length = area
    }
}

// 调用不提供外部名字
let rectarea = Rectangle(180.0)
print("面积为: \(rectarea.length)")

// 调用不提供外部名字
let rearea = Rectangle(370.0)
print("面积为: \(rearea.length)")

// 调用不提供外部名字
let recarea = Rectangle(110.0)
print("面积为: \(recarea.length)")

Le résultat de sortie de l'exécution du programme ci-dessus est :

面积为: 180.0
面积为: 370.0
面积为: 110.0

Type d'attribut facultatif

Si votre type personnalisé contient un attribut stocké qui autorise logiquement une valeur nulle, vous devez le définir comme type facultatif (type facultatif Sélectionnez le type d'attribut).

Lorsqu'une propriété stockée est déclarée facultative, elle sera automatiquement initialisée à zéro.

struct Rectangle {
    var length: Double?
    
    init(frombreadth breadth: Double) {
        length = breadth * 10
    }
    
    init(frombre bre: Double) {
        length = bre * 30
    }
    
    init(_ area: Double) {
        length = area
    }
}

let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")

let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)")

let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")

Le résultat de sortie de l'exécution du programme ci-dessus est :

面积为:Optional(180.0)
面积为:Optional(370.0)
面积为:Optional(110.0)

Modifier les attributs constants pendant le processus de construction

Tant que la valeur du constante peut être déterminée avant la fin du processus de construction, vous pouvez modifier la valeur d'une propriété constante à tout moment pendant la construction.

Pour une instance de classe, ses attributs constants ne peuvent être modifiés que lors du processus de construction de la classe qui la définit ; ils ne peuvent pas être modifiés dans les sous-classes.

Bien que l'attribut length soit désormais une constante, nous pouvons toujours définir sa valeur dans le constructeur de sa classe :

struct Rectangle {
    let length: Double?
    
    init(frombreadth breadth: Double) {
        length = breadth * 10
    }
    
    init(frombre bre: Double) {
        length = bre * 30
    }
    
    init(_ area: Double) {
        length = area
    }
}

let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")

let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)")

let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")

Le résultat de l'exécution du programme ci-dessus est :

面积为:Optional(180.0)
面积为:Optional(370.0)
面积为:Optional(110.0)

Constructeur par défaut

Le constructeur par défaut créera simplement une instance avec toutes les valeurs d'attribut définies sur les valeurs par défaut :

Dans l'exemple suivant, tous les attributs de la classe ShoppingListItem ont la valeur par défaut valeurs , et c'est une classe de base sans classe parent, elle obtiendra automatiquement un constructeur par défaut qui peut définir des valeurs par défaut pour toutes les propriétés

class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()


print("名字为: \(item.name)")
print("数理为: \(item.quantity)")
print("是否付款: \(item.purchased)")

Le résultat de sortie de l'exécution du programme ci-dessus est :

名字为: nil
数理为: 1
是否付款: false

structure Initialiseur membre par membre

Si une structure fournit des valeurs par défaut pour toutes les propriétés stockées et ne fournit pas elle-même un initialiseur personnalisé, elle peut automatiquement obtenir un membre par membre initialiseur.

Lorsque nous appelons le constructeur membre par membre, nous transmettons le nom du paramètre qui est le même que le nom de l'attribut membre pour terminer l'affectation initiale des attributs membre.

L'exemple suivant définit une structure Rectangle, qui contient deux attributs longueur et largeur. Swift peut déduire automatiquement le type Double de ces deux propriétés en fonction de leurs affectations initiales de 100,0 et 200,0.

struct Rectangle {
    var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0)

print("矩形的面积: \(area.length)")
print("矩形的面积: \(area.breadth)")

Étant donné que les deux propriétés stockées ont des valeurs par défaut, la structure Rectangle obtient automatiquement un constructeur membre par membre init(width:height:). Vous pouvez l'utiliser pour créer de nouvelles instances de Rectangle.

Le résultat de l'exécution du programme ci-dessus est :

名字为: nil
矩形的面积: 24.0
矩形的面积: 32.0

Proxy de constructeur de type valeur

Le constructeur peut terminer une partie du processus de construction de l'instance en appelant d'autres constructeurs. Ce processus est appelé délégation de constructeur et réduit la duplication de code entre plusieurs constructeurs.

Dans l'exemple suivant, la structure Rect appelle le processus de construction de Size et Point :

struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}

struct Rect {
    var origin = Point()
    var size = Size()
    init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}


// origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):
let basicRect = Rect()
print("Size 结构体初始值: \(basicRect.size.width, basicRect.size.height) ")
print("Rect 结构体初始值: \(basicRect.origin.x, basicRect.origin.y) ")

// 将origin和size的参数值赋给对应的存储型属性
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
    size: Size(width: 5.0, height: 5.0))

print("Size 结构体初始值: \(originRect.size.width, originRect.size.height) ")
print("Rect 结构体初始值: \(originRect.origin.x, originRect.origin.y) ")


//先通过center和size的值计算出origin的坐标。
//然后再调用(或代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
    size: Size(width: 3.0, height: 3.0))

print("Size 结构体初始值: \(centerRect.size.width, centerRect.size.height) ")
print("Rect 结构体初始值: \(centerRect.origin.x, centerRect.origin.y) ")

Le résultat de sortie de l'exécution du programme ci-dessus est :

Size 结构体初始值: (0.0, 0.0) 
Rect 结构体初始值: (0.0, 0.0) 
Size 结构体初始值: (5.0, 5.0) 
Rect 结构体初始值: (2.0, 2.0) 
Size 结构体初始值: (3.0, 3.0) 
Rect 结构体初始值: (2.5, 2.5)

Constructor règle de proxy

值类型类类型
不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给本身提供的其它构造器。 你可以使用self.init在自定义的构造器中引用其它的属于相同值类型的构造器。它可以继承自其它类,这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。

Héritage de classe et processus de construction

Swift fournit deux types de constructeurs de classe pour garantir que les propriétés stockées dans toutes les instances de classe peuvent obtenir des valeurs initiales. Il s'agit de constructeurs désignés et de constructeurs pratiques.

指定构造器便利构造器
类中最主要的构造器类中比较次要的、辅助型的构造器
初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。
每一个类都必须拥有至少一个指定构造器只在必要的时候为类提供便利构造器
Init(parameters) {
    statements
}
convenience init(parameters) {
      statements
}

Instance de constructeur spécifiée

class mainClass {
    var no1 : Int // 局部存储变量
    init(no1 : Int) {
        self.no1 = no1 // 初始化
    }
}
class subClass : mainClass {
    var no2 : Int // 新的子类存储变量
    init(no1 : Int, no2 : Int) {
        self.no2 = no2 // 初始化
        super.init(no1:no1) // 初始化超类
    }
}

let res = mainClass(no1: 10)
let res2 = subClass(no1: 10, no2: 20)

print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")

Le résultat de sortie de l'exécution du programme ci-dessus est :

res 为: 10
res 为: 10
res 为: 20

Instance de constructeur pratique

class mainClass {
    var no1 : Int // 局部存储变量
    init(no1 : Int) {
        self.no1 = no1 // 初始化
    }
}

class subClass : mainClass {
    var no2 : Int
    init(no1 : Int, no2 : Int) {
        self.no2 = no2
        super.init(no1:no1)
    }
    // 便利方法只需要一个参数
    override convenience init(no1: Int)  {
        self.init(no1:no1, no2:0)
    }
}
let res = mainClass(no1: 20)
let res2 = subClass(no1: 30, no2: 50)

print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")

Le résultat de sortie de l'exécution du programme ci-dessus est :

res 为: 20
res2 为: 30
res2 为: 50

Héritage et surcharge des constructeurs

Les sous-classes de Swift n'héritent pas du constructeur de la classe parent par défaut.

Le constructeur de la classe parent n'est hérité que lorsqu'il est certain et sûr.

Lorsque vous remplacez un constructeur désigné par une classe parent, vous devez écrire le modificateur de remplacement.

class SuperClass {
    var corners = 4
    var description: String {
        return "\(corners) 边"
    }
}
let rectangle = SuperClass()
print("矩形: \(rectangle.description)")

class SubClass: SuperClass {
    override init() {  //重载构造器
        super.init()
        corners = 5
    }
}

let subClass = SubClass()
print("五角型: \(subClass.description)")

Le résultat de l'exécution du programme ci-dessus est :

矩形: 4 边
五角型: 5 边

Instances de constructeur spécifiées et de constructeur de commodité

L'exemple suivant montrera le constructeur désigné et le constructeur de commodité en fonctionnement Héritage de constructeurs et constructeurs automatiques.

Il définit une hiérarchie de classes contenant deux classes, MainClass et SubClass, et démontrera comment leurs constructeurs interagissent.

class MainClass {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    convenience init() {
        self.init(name: "[匿名]")
    }
}
let main = MainClass(name: "php")
print("MainClass 名字为: \(main.name)")

let main2 = MainClass()
print("没有对应名字: \(main2.name)")

class SubClass: MainClass {
    var count: Int
    init(name: String, count: Int) {
        self.count = count
        super.init(name: name)
    }
    
    override convenience init(name: String) {
        self.init(name: name, count: 1)
    }
}

let sub = SubClass(name: "php")
print("MainClass 名字为: \(sub.name)")

let sub2 = SubClass(name: "php", count: 3)
print("count 变量: \(sub2.count)")

Le résultat de sortie de l'exécution du programme ci-dessus est :

MainClass 名字为: php
没有对应名字: [匿名]
MainClass 名字为: php
count 变量: 3

Constructeur défaillant de la classe

Si un objet de type classe, structure ou énumération, dans Il est possible d'échouer lors de la construction de lui-même, définissez donc un initialiseur défaillant pour celui-ci.

Les raisons possibles de l'échec de l'initialisation de la variable sont :

  • Valeur de paramètre transmise non valide.

  • Il manque une ressource externe requise.

  • Des conditions spécifiques ne sont pas remplies.

Afin de gérer correctement la situation qui pourrait échouer au cours de ce processus de construction.

Vous pouvez ajouter un ou plusieurs initialiseurs défaillants à la définition d'un type de classe, de structure ou d'énumération. La syntaxe consiste à ajouter un point d'interrogation (init ?) après le mot-clé init.

Exemple

Dans l'exemple suivant, une structure nommée Animal est définie, qui a un attribut constant de type String nommé espèce.

Dans le même temps, la structure définit également un constructeur faillible avec une espèce de paramètre de type String. Cet initialiseur faillable est utilisé pour vérifier si le paramètre entrant est une chaîne vide. S'il s'agit d'une chaîne vide, l'initialiseur failable ne parvient pas à construire l'objet, sinon il réussit.

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

//通过该可失败构造器来构建一个Animal的对象,并检查其构建过程是否成功
// someCreature 的类型是 Animal? 而不是 Animal
let someCreature = Animal(species: "长颈鹿")

// 打印 "动物初始化为长颈鹿"
if let giraffe = someCreature {
    print("动物初始化为\(giraffe.species)")
}

Le résultat de l'exécution du programme ci-dessus est :

动物初始化为长颈鹿

Constructeur faillible de type énumération

Vous pouvez construire un constructeur faillable avec un ou plusieurs paramètres Conteneur pour obtenir une énumération spécifique membres d’un type d’énumération.

Exemple

Dans l'exemple suivant, un type d'énumération nommé TemperatureUnit est défini. Il contient trois membres d'énumération possibles (Kelvin, Celsius et Fahrenheit) et un initialiseur défaillant qui est utilisé pour trouver le membre d'énumération correspondant à la valeur du caractère :

enum TemperatureUnit {
	// 开尔文,摄氏,华氏
    case Kelvin, Celsius, Fahrenheit
    init?(symbol: Character) {
        switch symbol {
        case "K":
            self = .Kelvin
        case "C":
            self = .Celsius
        case "F":
            self = .Fahrenheit
        default:
            return nil
        }
    }
}


let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
    print("这是一个已定义的温度单位,所以初始化成功。")
}

let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
    print("这不是一个已定义的温度单位,所以初始化失败。")
}

Les résultats de l'exécution du programme ci-dessus pour :

这是一个已定义的温度单位,所以初始化成功。
这不是一个已定义的温度单位,所以初始化失败。

Initialiseur défaillant de classe

Initialiseur défaillant de type valeur (comme une structure ou un type d'énumération), quand et où l'échec de la construction est déclenché Il n'y a aucune restriction sur le comportement.

Cependant, l'initialiseur faillable d'une classe ne peut déclencher un comportement d'échec qu'une fois que toutes les propriétés de la classe ont été initialisées et que les appels proxy entre les constructeurs entre toutes les classes ont eu lieu.

Exemple

Dans l'exemple suivant, une classe nommée StudRecord est définie. L'attribut studname étant une constante, une fois la classe StudRecord construite avec succès, l'attribut studname doit avoir une valeur non nulle. valeur.

class StudRecord {
    let studname: String!
    init?(studname: String) {
        self.studname = studname
        if studname.isEmpty { return nil }
    }
}
if let stname = StudRecord(studname: "失败构造器") {
    print("模块为 \(stname.studname)")
}

Le résultat de l'exécution du programme ci-dessus est :

模块为 失败构造器

Remplacer un constructeur faillible

Tout comme les autres constructeurs, vous pouvez également utiliser des constructeurs faillibles de sous-classes. Le constructeur remplace l'initialiseur défaillant de la classe de base.

Alternativement, vous pouvez remplacer l'initialiseur faillable d'une classe de base par l'initialiseur non faillible d'une sous-classe.

Vous pouvez remplacer un initialiseur défaillant par un initialiseur non-failable, mais l'inverse ne fonctionne pas.

Un initialiseur non faillable ne peut jamais déléguer un appel à un initialiseur faillable.

Exemples

Les exemples suivants décrivent des constructeurs faillables et non faillables :

class Planet {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    convenience init() {
        self.init(name: "[No Planets]")
    }
}
let plName = Planet(name: "Mercury")
print("行星的名字是: \(plName.name)")

let noplName = Planet()
print("没有这个名字的行星: \(noplName.name)")

class planets: Planet {
    var count: Int
    
    init(name: String, count: Int) {
        self.count = count
        super.init(name: name)
    }
    
    override convenience init(name: String) {
        self.init(name: name, count: 1)
    }
}

Le résultat de sortie de l'exécution du programme ci-dessus est :

行星的名字是: Mercury
没有这个名字的行星: [No Planets]

Initialiseur faillable !

Habituellement, nous définissons un initialiseur faillable en ajoutant un point d'interrogation (init ?) après le mot-clé init, mais vous pouvez également l'utiliser en ajoutant un point d'exclamation après init pour définir un initialiseur faillable. (initialisation !). L'exemple est le suivant :

struct StudRecord {
    let stname: String
    
    init!(stname: String) {
        if stname.isEmpty {return nil }
        self.stname = stname
    }
}

let stmark = StudRecord(stname: "php")
if let name = stmark {
    print("指定了学生名")
}

let blankname = StudRecord(stname: "")
if blankname == nil {
    print("学生名为空")
}

Le résultat de sortie de l'exécution du programme ci-dessus est :

指定了学生名
学生名为空