Comptage automatique rapide des références
Swift utilise le mécanisme de comptage automatique de références (ARC) pour suivre et gérer la mémoire de l'application
Normalement, nous n'avons pas besoin de libérer manuellement la mémoire, car ARC sera utilisé lorsque l'instance de la classe n'est pas Lorsqu'elle est réutilisée, la mémoire qu'elle occupe est automatiquement libérée.
Mais parfois, nous devons encore implémenter la gestion de la mémoire dans le code.
Fonction ARC
Chaque fois que vous utilisez la méthode init() pour créer une nouvelle instance d'une classe, ARC allouera une grande partie de la mémoire pour stocker l'instance. information.
La mémoire contiendra les informations de type de l'instance et les valeurs de tous les attributs associés de cette instance.
Lorsque l'instance n'est plus utilisée, ARC libère la mémoire occupée par l'instance et permet d'utiliser la mémoire libérée à d'autres fins.
Pour garantir que les instances utilisées ne seront pas détruites, ARC suivra et calculera le nombre de propriétés, constantes et variables référencées par chaque instance.
L'attribution d'une instance à une propriété, une constante ou une variable créera une référence forte à cette instance. Tant que la référence forte existe, l'instance ne peut pas être détruite.
Instance ARC
class Person { let name: String init(name: String) { self.name = name print("\(name) 开始初始化") } deinit { print("\(name) 被析构") } } // 值会被自动初始化为nil,目前还不会引用到Person类的实例 var reference1: Person? var reference2: Person? var reference3: Person? // 创建Person类的新实例 reference1 = Person(name: "php") //赋值给其他两个变量,该实例又会多出两个强引用 reference2 = reference1 reference3 = reference1 //断开第一个强引用 reference1 = nil //断开第二个强引用 reference2 = nil //断开第三个强引用,并调用析构函数 reference3 = nil
Le résultat de l'exécution du programme ci-dessus est :
php 开始初始化 php 被析构
Référence circulaire forte entre les instances de classe
Dans l'exemple ci-dessus, ARC suivra le nombre de références à votre instance Person nouvellement créée et la détruira lorsque l'instance Person n'est plus nécessaire.
Cependant, nous pouvons écrire du code tel qu'une classe n'aura jamais 0 référence forte. Cela se produit lorsque deux instances de classe conservent des références fortes l’une à l’autre et s’empêchent mutuellement d’être détruites. C’est ce qu’on appelle un cycle de référence fort.
Exemple
Ce qui suit montre un exemple de création par inadvertance d'un cycle de référence fort. L'exemple définit deux classes : Personne et Appartement, qui sont utilisées pour modéliser un appartement et ses résidents :
class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { print("\(name) 被析构") } } class Apartment { let number: Int init(number: Int) { self.number = number } var tenant: Person? deinit { print("Apartment #\(number) 被析构") } } // 两个变量都被初始化为nil var php: Person? var number73: Apartment? // 赋值 php = Person(name: "php") number73 = Apartment(number: 73) // 意感叹号是用来展开和访问可选变量 php 和 number73 中的实例 // 循环强引用被创建 php!.apartment = number73 number73!.tenant = php // 断开 php 和 number73 变量所持有的强引用时,引用计数并不会降为 0,实例也不会被 ARC 销毁 // 注意,当你把这两个变量设为nil时,没有任何一个析构函数被调用。 // 强引用循环阻止了Person和Apartment类实例的销毁,并在你的应用程序中造成了内存泄漏 php = nil number73 = nil
Résoudre le cycle de référence fort entre les instances
Swift propose deux façons d'utiliser Pour résoudre le Problème de référence forte cyclique que vous rencontrez lors de l'utilisation d'attributs de classe :
Référence faible
Référence sans propriétaire
Les références faibles et les références sans propriétaire permettent à une instance dans une référence circulaire de faire référence à une autre instance sans maintenir une référence forte. De cette manière, les instances peuvent se référer les unes aux autres sans créer un cycle de référence fort.
Utilisez des références faibles pour les instances qui deviendront nulles au cours de leur vie. Au contraire, utilisez une référence sans propriétaire pour une instance qui ne sera jamais affectée à nil après l'affectation initiale.
Instance de référence faible
class Module { let name: String init(name: String) { self.name = name } var sub: SubModule? deinit { print("\(name) 主模块") } } class SubModule { let number: Int init(number: Int) { self.number = number } weak var topic: Module? deinit { print("子模块 topic 数为 \(number)") } } var toc: Module? var list: SubModule? toc = Module(name: "ARC") list = SubModule(number: 4) toc!.sub = list list!.topic = toc toc = nil list = nil
Le résultat de sortie de l'exécution du programme ci-dessus est :
ARC 主模块 子模块 topic 数为 4
Instance de référence sans propriétaire
class Student { let name: String var section: Marks? init(name: String) { self.name = name } deinit { print("\(name)") } } class Marks { let marks: Int unowned let stname: Student init(marks: Int, stname: Student) { self.marks = marks self.stname = stname } deinit { print("学生的分数为 \(marks)") } } var module: Student? module = Student(name: "ARC") module!.section = Marks(marks: 98, stname: module!) module = nil
Le résultat de sortie du programme ci-dessus l'exécution est :
ARC 学生的分数为 98
Cycle de référence fort provoqué par la fermeture
Un cycle de référence fort se produit également lorsque vous attribuez une fermeture à un attribut d'une instance de classe et que le corps de la fermeture contient un autre exemple sont utilisés. Ce corps de fermeture peut accéder à une propriété de l'instance, telle que self.someProperty, ou appeler une méthode de l'instance, telle que self.someMethod, dans la fermeture. Les deux cas aboutissent à une fermeture qui se « capture », créant un cycle de référence fort.
Exemple
L'exemple suivant vous montre comment créer un cycle de référence fort lorsqu'une fermeture fait référence à soi. L'exemple définit une classe appelée HTMLElement, qui utilise un modèle simple pour représenter un seul élément en HTML :
class HTMLElement { let name: String let text: String? lazy var asHTML: () -> String = { if let text = self.text { return "<\(self.name)>\(text)</\(self.name)>" } else { return "<\(self.name) />" } } init(name: String, text: String? = nil) { self.name = name self.text = text } deinit { print("\(name) is being deinitialized") } } // 创建实例并打印信息 var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") print(paragraph!.asHTML())
La classe HTMLElement génère une boucle forte entre l'instance de classe et la fermeture de la valeur par défaut asHTML. L'attribut asHTML de l'instance
contient une forte référence à la fermeture. Cependant, la fermeture utilise self dans son corps de fermeture (faisant référence à self.name et self.text), de sorte que la fermeture capture self, ce qui signifie qu'elle contient à son tour une forte référence à l'instance HTMLElement. De cette manière, les deux objets ont une forte référence circulaire.
Résoudre le cycle de référence fort provoqué par la fermeture : lors de la définition de la fermeture, définissez la liste de capture dans le cadre de la fermeture. De cette manière, le cycle de référence fort entre la fermeture et l'instance de classe peut être résolu.
Références faibles et références sans propriétaire
Définir une capture au sein d'une fermeture comme sans propriétaire lorsque la fermeture et les instances capturées se réfèrent toujours l'une à l'autre et sont toujours détruites en même temps. Citer.
À l'inverse, lorsque la référence de capture peut parfois être nulle, définir la capture au sein de la fermeture comme une référence faible.
Si la référence capturée ne sera jamais définie sur zéro, vous devez utiliser une référence sans propriétaire au lieu d'une référence faible.
Exemple
Dans l'exemple HTMLElement précédent, une référence sans propriétaire est la bonne façon de résoudre le cycle de référence fort. Écrivez la classe HTMLElement comme ceci pour éviter les cycles de référence forts :
class HTMLElement { let name: String let text: String? lazy var asHTML: () -> String = { [unowned self] in if let text = self.text { return "<\(self.name)>\(text)</\(self.name)>" } else { return "<\(self.name) />" } } init(name: String, text: String? = nil) { self.name = name self.text = text } deinit { print("\(name) 被析构") } } //创建并打印HTMLElement实例 var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") print(paragraph!.asHTML()) // HTMLElement实例将会被销毁,并能看到它的析构函数打印出的消息 paragraph = nil
Le résultat de l'exécution du programme ci-dessus est :
<p>hello, world</p> p 被析构