Initialisation Swift
L'initialisation est le processus de préparation d'une instance d'une classe, d'une structure ou d'une énumération en vue de son utilisation. Ce processus consiste à définir une valeur initiale pour chaque propriété stockée de cette instance et à effectuer toute autre tâche de configuration ou d'initialisation requise.
Les initialiseurs Swift utilisent la méthode init().
Contrairement aux initialiseurs Objective-C, les initialiseurs Swift ne renvoient pas de valeur. Leur tâche principale est de garantir que les nouvelles instances sont correctement initialisées avant d'être utilisées pour la première fois.
Les instances de classe peuvent également définir un désinitialiseur pour effectuer un travail de nettoyage juste avant que l'instance de classe ne soit désallouée.
Définition des valeurs initiales pour les propriétés stockées
Les classes et les structures doivent définir une valeur initiale appropriée pour toutes les propriétés stockées lorsqu'une instance est créée.
Lorsque des valeurs sont assignées à des propriétés stockées dans un initialiseur, leurs valeurs sont définies directement et ne déclenchent aucun observateur de propriété.
Le processus d'affectation des propriétés stockées dans un initialiseur :
Créer des valeurs initiales.
Spécifier des valeurs de propriété par défaut dans la définition de la propriété.
Initialiser l'instance et appeler la méthode init().
Initialiseurs
Un initialiseur est appelé lors de la création d'une nouvelle instance d'un type particulier. Dans sa forme la plus simple, il est semblable à une méthode d'instance sans paramètres, écrite à l'aide du mot-clé init.
Syntaxe
init()
{
// 实例化后执行的代码
}
Exemple
La structure suivante définit un initialiseur init sans paramètres, et à l'intérieur, elle initialise les valeurs des propriétés stockées length et breadth à 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 programme ci-dessus produit la sortie suivante :
矩形面积为 72.0
Valeurs par défaut des propriétés
Vous pouvez définir des valeurs initiales pour les propriétés stockées dans un initialiseur ; de même, vous pouvez également définir des valeurs par défaut lors de la déclaration de la propriété.
L'utilisation de valeurs par défaut rend votre initialiseur plus concis et plus clair, et le type de la propriété peut être automatiquement déduit de sa valeur par défaut.
Dans l'exemple suivant, nous définissons des valeurs par défaut lors de la déclaration des propriétés :
struct rectangle {
// 设置默认值
var length = 6
var breadth = 12
}
var area = rectangle()
print("矩形的面积为 \(area.length*area.breadth)")
Le programme ci-dessus produit la sortie suivante :
矩形面积为 72
Paramètres de l'initialiseur
Vous pouvez fournir des paramètres d'initialiseur lors de la définition d'un initialiseur 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 programme ci-dessus produit la sortie suivante :
面积为: 72.0 面积为: 432.0
Noms de paramètres internes et externes
Comme pour les paramètres des fonctions et des méthodes, les paramètres d'initialiseur ont également un nom de paramètre interne utilisé à l'intérieur de l'initialiseur et un nom de paramètre externe utilisé lors de l'appel de l'initialiseur.
Toutefois, les initialiseurs n'ont pas de nom distinctif avant les parenthèses, contrairement aux fonctions et aux méthodes. Par conséquent, lors de l'appel d'un initialiseur, les noms et types des paramètres de l'initialiseur sont principalement utilisés pour déterminer quel initialiseur doit être appelé.
Si vous ne fournissez pas de noms externes pour les paramètres lors de la définition d'un initialiseur, Swift génère automatiquement un nom externe identique au nom interne pour chaque paramètre d'initialiseur.
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 programme ci-dessus produit la sortie suivante :
red 值为: 1.0 green 值为: 0.0 blue 值为: 1.0 red 值为: 0.5 green 值为: 0.5 blue 值为: 0.5
Paramètres d'initialiseur sans noms externes
Si vous ne voulez pas fournir de nom externe pour un paramètre d'un initialiseur, vous pouvez utiliser un trait de soulignement_pour décrire explicitement son nom externe.
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 programme ci-dessus produit la sortie suivante :
面积为: 180.0 面积为: 370.0 面积为: 110.0
Types de propriétés optionnelles
Si votre type personnalisé contient une propriété stockée qui est logiquement autorisée à ne pas avoir de valeur, vous devez la définir comme un type optionnel (type de propriété optionnelle).
Lorsqu'une propriété stockée est déclarée comme optionnelle, elle est automatiquement initialisée à nil.
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 programme ci-dessus produit la sortie suivante :
面积为:Optional(180.0) 面积为:Optional(370.0) 面积为:Optional(110.0)
Affectation de propriétés constantes pendant l'initialisation
Tant que la valeur d'une constante peut être déterminée à la fin de l'initialisation, vous pouvez modifier la valeur de la propriété constante à n'importe quel moment pendant l'initialisation.
Pour une instance de classe, sa propriété constante ne peut être modifiée que pendant l'initialisation de la classe qui la définit ; elle ne peut pas être modifiée dans une sous-classe.
Même si la propriété length est maintenant une constante, nous pouvons toujours définir sa valeur dans l'initialiseur 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 programme ci-dessus produit la sortie suivante :
面积为:Optional(180.0) 面积为:Optional(370.0) 面积为:Optional(110.0)
Initialiseurs par défaut
Un initialiseur par défaut crée simplement une instance avec toutes les valeurs de propriété définies sur leurs valeurs par défaut :
Dans l'exemple suivant, toutes les propriétés de la classe ShoppingListItem ont des valeurs par défaut, et comme il s'agit d'une classe de base sans superclasse, elle obtient automatiquement un initialiseur 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 programme ci-dessus produit la sortie suivante :
名字为: nil 数理为: 1 是否付款: false
Initialiseurs membre par membre pour les types de structure
Si une structure fournit des valeurs par défaut pour toutes ses propriétés stockées et ne fournit aucun initialiseur personnalisé, elle reçoit automatiquement un initialiseur membre par membre.
Lors de l'appel d'un initialiseur membre par membre, vous transmettez des valeurs en utilisant des noms de paramètres qui correspondent aux noms des propriétés membres, ce qui permet d'effectuer l'affectation initiale des propriétés membres.
L'exemple suivant définit une structure Rectangle avec deux propriétés, length et breadth. Swift peut automatiquement inférer leurs types comme Double à partir des valeurs initiales 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)")
Comme les deux propriétés stockées ont des valeurs par défaut, la structure Rectangle reçoit automatiquement un initialiseur membre par membre init(width:height:). Vous pouvez l'utiliser pour créer de nouvelles instances de Rectangle.
Le programme ci-dessus produit la sortie suivante :
矩形的面积: 24.0 矩形的面积: 32.0
Délégation d'initialiseur pour les types valeur
Un initialiseur peut appeler d'autres initialiseurs pour effectuer une partie du processus d'initialisation de l'instance. Ce processus est appelé délégation d'initialiseurs, et il réduit la duplication de code entre plusieurs initialiseurs.
Dans l'exemple suivant, la structure Rect appelle les initialiseurs 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 programme ci-dessus produit la sortie suivante :
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)
Règles de délégation d'initialiseurs
| Types valeur | Types de classe |
|---|---|
| Les types valeur ne prennent pas en charge l'héritage, donc le processus de délégation d'initialiseurs est relativement simple, car ils ne peuvent déléguer qu'à d'autres initialiseurs fournis par eux-mêmes. Vous pouvez utiliser self.init dans un initialiseur personnalisé pour référencer d'autres initialiseurs du même type valeur. | Il peut hériter d'autres classes, ce qui signifie qu'une classe est responsable de s'assurer que toutes ses propriétés stockées héritées sont également correctement initialisées lors de la construction. |
Héritage de classe et initialisation
Swift fournit deux types d'initialiseurs de classe pour garantir que toutes les propriétés stockées d'une instance de classe peuvent obtenir des valeurs initiales. Il s'agit des initialiseurs désignés et des initialiseurs de commodité.
| Initialiseurs désignés | Initialiseurs de commodité |
| L'initialiseur principal d'une classe. | Un initialiseur secondaire, auxiliaire, pour une classe. |
| Il initialise toutes les propriétés introduites par la classe et appelle un initialiseur de superclasse dans la chaîne des superclasses pour initialiser la superclasse. | Vous pouvez définir un initialiseur de commodité pour appeler un initialiseur désigné dans la même classe et fournir des valeurs par défaut pour ses paramètres. Vous pouvez également définir un initialiseur de commodité pour créer une instance à des fins spécifiques ou pour une entrée spécifique. |
| Chaque classe doit avoir au moins un initialiseur désigné. | Ne fournissez des initialiseurs de commodité pour une classe que lorsque c'est nécessaire. |
Init(parameters) {
statements
}
|
convenience init(parameters) {
statements
}
|
Exemple d'initialiseur désigné
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 programme ci-dessus produit la sortie suivante :
res 为: 10 res 为: 10 res 为: 20
Exemple d'initialiseur de commodité
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 programme ci-dessus produit la sortie suivante :
res 为: 20 res2 为: 30 res2 为: 50
Héritage et redéfinition d'initialiseurs
Une sous-classe en Swift n'hérite pas des initialiseurs de la superclasse par défaut.
Les initialiseurs de superclasse ne sont hérités que dans certaines circonstances sûres.
Lorsque vous redéfinissez un initialiseur désigné de la superclasse, vous devez écrire le modificateur override.
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 programme ci-dessus produit la sortie suivante :
矩形: 4 边 五角型: 5 边
Exemple d'initialiseur désigné et d'initialiseur de commodité
L'exemple suivant va démontrer les initialiseurs désignés, les initialiseurs de commodité et l'héritage automatique des initialiseurs en action.
Il définit une hiérarchie de classes contenant deux classes, MainClass et SubClass, et montrera comment leurs initialiseurs interagissent.
class MainClass {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[匿名]")
}
}
let main = MainClass(name: "Example")
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: "Example")
print("MainClass 名字为: \(sub.name)")
let sub2 = SubClass(name: "Example", count: 3)
print("count 变量: \(sub2.count)")
Le programme ci-dessus produit la sortie suivante :
MainClass 名字为: Example 没有对应名字: [匿名] MainClass 名字为: Example count 变量: 3
Initialiseurs pouvant échouer
Si un objet d'une classe, d'une structure ou d'un type d'énumération peut échouer lors de son processus de construction, définissez un initialiseur pouvant échouer pour celui-ci.
Les causes possibles d'échec de l'initialisation d'une variable incluent :
Le passage de valeurs de paramètres non valides.
L'absence d'une sorte de ressource externe requise.
Une condition spécifique n'a pas été remplie.
Pour gérer correctement les situations où le processus de construction peut échouer.
Vous pouvez ajouter un ou plusieurs initialiseurs pouvant échouer à la définition d'une classe, d'une structure ou d'une énumération. La syntaxe consiste à ajouter un point d'interrogation après le mot-clé init (init?).
Exemple
L'exemple suivant définit une structure nommée Animal, qui possède une propriété constante nommée species de type String.
La structure définit également un initialiseur pouvant échouer qui prend un paramètre String nommé species. Cet initialiseur pouvant échouer sert à vérifier si le paramètre transmis est une chaîne vide. S'il s'agit d'une chaîne vide, l'initialiseur pouvant échouer 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)")
}
La sortie de l'exécution du programme ci-dessus est :
Animal initialized as giraffe
Initialiseurs pouvant échouer pour les types d'énumération
Vous pouvez obtenir un membre d'énumération spécifique dans un type d'énumération en construisant un initialiseur pouvant échouer avec un ou plusieurs paramètres.
Exemple
L'exemple suivant définit un type d'énumération nommé TemperatureUnit, qui contient trois membres d'énumération possibles (Kelvin, Celsius et Fahrenheit) et un initialiseur pouvant échouer utilisé pour trouver le membre d'énumération correspondant à une valeur Character :
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("这不是一个已定义的温度单位,所以初始化失败。")
}
La sortie de l'exécution du programme ci-dessus est :
This is a defined temperature unit, so initialization succeeded. This is not a defined temperature unit, so initialization failed.
Initialiseurs pouvant échouer pour les types de classe
Les initialiseurs pouvant échouer pour les types de valeur (tels que les structures ou les énumérations) n'ont aucune restriction sur le moment et le lieu où ils déclenchent un échec d'initialisation.
Cependant, un initialiseur pouvant échouer pour une classe ne peut déclencher un échec qu'après que toutes les propriétés de la classe ont été initialisées et que tous les appels de délégation entre initialiseurs de la classe ont eu lieu.
Exemple
Dans l'exemple suivant, une classe nommée StudRecord est définie. Comme la propriété studname est une constante, une fois la classe StudRecord construite avec succès, la propriété studname a nécessairement une valeur non-nil.
class StudRecord {
let studname: String!
init?(studname: String) {
self.studname = studname
if studname.isEmpty { return nil }
}
}
if let stname = StudRecord(studname: "失败构造器") {
print("模块为 \(stname.studname)")
}
La sortie de l'exécution du programme ci-dessus est :
Module is a failed constructor
Redéfinir un initialiseur pouvant échouer
Tout comme les autres initialiseurs, vous pouvez également redéfinir un initialiseur pouvant échouer d'une classe de base par un initialiseur pouvant échouer d'une sous-classe.
Ou vous pouvez redéfinir un initialiseur pouvant échouer d'une classe de base par un initialiseur ne pouvant pas échouer d'une sous-classe.
Vous pouvez utiliser un initialiseur ne pouvant pas échouer pour redéfinir un initialiseur pouvant échouer, mais l'inverse ne fonctionne pas.
Un initialiseur ne pouvant pas échouer ne peut jamais déléguer à un initialiseur pouvant échouer.
Exemple
L'exemple suivant décrit les initialiseurs pouvant échouer et ceux ne pouvant pas échouer :
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)
}
}
La sortie de l'exécution du programme ci-dessus est :
行星的名字是: Mercury 没有这个名字的行星: [No Planets]
L'initialiseur pouvant échouer init!
Généralement, on définit un initialiseur pouvant échouer en ajoutant un point d'interrogation après le mot-clé init (init?), mais vous pouvez aussi définir un initialiseur pouvant échouer en ajoutant un point d'exclamation après init (init!). Exemple :
struct StudRecord {
let stname: String
init!(stname: String) {
if stname.isEmpty {return nil }
self.stname = stname
}
}
let stmark = StudRecord(stname: "Example")
if let name = stmark {
print("指定了学生名")
}
let blankname = StudRecord(stname: "")
if blankname == nil {
print("学生名为空")
}
La sortie de l'exécution du programme ci-dessus est :
Student name specified Student name is emptyAutres extensions