diff --git a/9. Table View/Content/Images/P2/P2C4_10.png b/9. Table View/Content/Images/P2/P2C4_10.png new file mode 100644 index 0000000..a4bddf1 Binary files /dev/null and b/9. Table View/Content/Images/P2/P2C4_10.png differ diff --git a/9. Table View/Content/Images/P2/P2C4_9.png b/9. Table View/Content/Images/P2/P2C4_9.png index 67e9b65..090a8b6 100644 Binary files a/9. Table View/Content/Images/P2/P2C4_9.png and b/9. Table View/Content/Images/P2/P2C4_9.png differ diff --git a/9. Table View/Content/part1.md b/9. Table View/Content/part1.md index 1ba1732..098eca9 100755 --- a/9. Table View/Content/part1.md +++ b/9. Table View/Content/part1.md @@ -53,13 +53,13 @@ Pour arriver à nos fins, nous allons nous suivre le plan suivant : Vous êtes prêts ? Alors, allons-y ! ### Découvrez les protocoles -C'est parti ! Nous allons parler des protocoles ! +C'est parti ! Nous allons parler des protocoles ! > **:information_source:** Si vous suivez [le parcours](https://openclassrooms.com/paths/developpeur-se-dapplication-ios), je sais, on en a déjà un peu parlé. Mais cette fois-ci, on va vraiment rentrer dans le détail ! #### Une histoire d'héritage... -Pour découvrir les protocoles, je vous ai concocté un petit Playground ! +Pour découvrir les protocoles, je vous ai concocté un petit Playground ! > **:information_source:** Je vous invite à le télécharger [ici](https://s3-eu-west-1.amazonaws.com/static.oc-static.com/prod/courses/files/Parcours+DA+iOS/Cours+9+-+Table+View/Animal+-+Starter.playground.zip). @@ -106,9 +106,9 @@ Pour déclarer un protocole, on utilise le mot-clé `protocol` suivi d'un nom et protocol Animal { } -``` +``` -Oui, ça ressemble à une classe. Et la ressemblance ne s'arrête pas là ! +Oui, ça ressemble à une classe. Et la ressemblance ne s'arrête pas là ! ##### Méthodes @@ -123,7 +123,7 @@ protocol Animal { > **:question:** Hé, mais attends ! Tu as oublié l'implémentation des méthodes. -Eh oui comme je l'ai dit, un protocole, c'est une liste d'exigences. Ensuite, on va demander à une classe de **se conformer** à ces exigences. +Eh oui comme je l'ai dit, un protocole, c'est une liste d'exigences. Ensuite, on va demander à une classe de **se conformer** à ces exigences. > **:information_source:** En gros, un protocole peut définir des méthodes, mais ce sont les classes qui les implémentent. @@ -143,11 +143,11 @@ protocol Animal { Je définis ici dans mon protocole une propriété description de type `String`. Jusque là rien de nouveau. Ensuite, j'ajoute entre accolades les mots `get` et `set`. Cela signifie que cette propriété pourra être modifiée. > **:information_source:** Si j'avais voulu définir une propriété en lecture seule, j'aurais écrit : -> +> > ```swift -> var description: String { get } +> var description: String { get } > ``` - + > **:question:** Je veux bien toute cette histoire de protocole, mais à quoi ça sert vraiment ? Je comprends que ce soit un peu flou pour le moment, mais on y vient ! @@ -161,15 +161,15 @@ Je le répète : un protocole, c'est **une liste d'exigences**. Autrement dit, u Par exemple, dans notre cas : > Si tu veux être un `Animal`, il faut que tu saches faire les choses suivantes : -> +> > - `makeSound` > - `move` -> +> > Et donner une valeur à la propriété suivante : -> +> > - `description` -Nous avons dans notre Playground deux classes qui voudraient bien être des animaux : `Dog` et `Bird`. +Nous avons dans notre Playground deux classes qui voudraient bien être des animaux : `Dog` et `Bird`. ```swift class Dog { @@ -186,7 +186,7 @@ class Bird { ``` > **:information_source:** J'ai supprimé de ces classes tout ce qui était lié à l'héritage d'`Animal,` car nous avons supprimé cette classe. - + Pour que `Dog` et `Bird` soient des animaux, ils doivent **se conformer au protocole**. Cela se fait en deux étapes : 1. Adopter le protocole @@ -259,13 +259,13 @@ On a remplacé notre classe inutile et trop générique par un protocole qui rem #### En résumé - Un protocole est une **liste d'exigences**. Ces exigences peuvent être des méthodes ou des propriétés. - Souvenez-vous de la phrase suivante. Un protocole est une entité qui déclare : "*Si tu veux être ça, il faut que tu fasses ça !*" -- Un protocole se déclare avec la syntaxe suivante : +- Un protocole se déclare avec la syntaxe suivante : ```swift protocol NomDuProtocole { func maMethode() func maMethodeAvecParametresEtValeurDeRetour(param1: Type, Param2: Type) -> Type - + var maPropriete: Type { get set } var maProprieteEnLectureSeule: Type { get } } @@ -314,7 +314,7 @@ protocol UnProtocole { (...) } var uneVariable: UnProtocole var unTableau: [UnProtocole] func uneFonction(param: UnProtocole) -> UnProtocole { (...) } -``` +``` La seule différence, c'est que vous ne vous pouvez pas créer d'instance à partir d'un protocole. En effet, une classe/structure/énumération définit des objets. Vous pouvez donc en créer des instances. Alors que les protocoles définissent seulement des listes d'exigences. @@ -325,7 +325,7 @@ var uneVariable = UnProtocole() ``` > **:question:** Euh... Mais du coup, ça ne veut rien dire ça : -> +> > ```swift > var uneVariable: UnProtocole > ``` @@ -354,7 +354,7 @@ Du coup, on peut faire des trucs sympas avec les protocoles comme ceci : ```swift var monTableauDAnimaux: [Animal] = [Dog(), Bird()] -``` +``` On a un tableau qui contient des objets qui ne sont pas du même type, mais qui se conforment tous au type `Animal`. Lorsque je vais programmer en utilisant mon tableau d'animaux, je vais programmer autour de *l'interface* définie par mon protocole : je ne me soucie pas de savoir quel type d'animal je manipule, et c'est une bien meilleure pratique qui rend mon code plus *modulaire* ! @@ -370,25 +370,25 @@ class MaClasse: MonProtocole {} struct MaStructure: MonProtocole {} enum MonEnumeration: MonProtocole {} -``` +``` -Cela fonctionne exactement de la même manière ! +Cela fonctionne exactement de la même manière ! Avant, seulement les classes pouvaient partager des comportements grâce à l'héritage. Mais les structures et les énumérations n'ont pas l'héritage. Mais grâce aux protocoles, toutes les structures de donnée peuvent partager des comportements ! Encore mieux, une classe peut partager des méthodes avec une énumération. > **:information_source:** On peut même aller encore plus loin ! Un protocole peut se conformer à un autre protocole : -> +> > ```swift > protocol MonProtocole {} -> +> > protocol UnAutreProtocole : MonProtocole {} > ``` #### Plusieurs protocoles -Une même classe/structure/énumération peut se conformer à plusieurs protocoles. En effet, un protocole est simplement une liste d'exigences, du coup, on peut combiner ces listes pour obtenir une plus grande liste. +Une même classe/structure/énumération peut se conformer à plusieurs protocoles. En effet, un protocole est simplement une liste d'exigences, du coup, on peut combiner ces listes pour obtenir une plus grande liste. Je vous propose qu'on rajoute du coup un deuxième protocole. On va appeler ce protocole `Nameable`. Et il sera implémenté par toute classe qui veut pouvoir avoir un nom et un prénom : @@ -399,13 +399,13 @@ protocol Nameable { func getFullName() -> String } -``` +``` Notre classe `Dog` va adopter le protocole. Lorsqu'une classe adopte plusieurs protocoles, on les sépare par une virgule : ```swift class Dog: Animal, Nameable { (...) } -``` +``` Et ensuite il suffit de répondre à ses exigences. @@ -417,7 +417,7 @@ class Dog: Animal, Nameable { func getFullName() -> String { return firstName + " " + lastName } - + // (...) } ``` @@ -436,7 +436,7 @@ Le monde de la programmation est généralement sous le paradigme de l'orienté Mais en Swift, les protocoles sont tellement puissants que beaucoup considèrent que **Swift est un langage de programmation orienté protocole** (*POP : Protocol Oriented Programming*). -Pour comprendre ce que cela change, je vous propose de créer dans notre Playground, une classe `Human`. +Pour comprendre ce que cela change, je vous propose de créer dans notre Playground, une classe `Human`. ```swift class Human { @@ -444,7 +444,7 @@ class Human { print("Bonjour !") } } -``` +``` Un humain n'est pas un animal. Mais en revanche, il a un nom et un prénom. Donc nous allons lui faire adopter le protocole `Nameable`. @@ -456,7 +456,7 @@ class Human: Nameable { func getFullName() -> String { return firstName + " " + lastName } - + func speak() { print("Bonjour !") } @@ -530,7 +530,7 @@ class Dog: Nameable { var lastName: String = "" // Lorsque j'appelle cette méthode sur un chien, - // c'est cette implémentation qui sera appellée, + // c'est cette implémentation qui sera appellée, // et non celle définie par défaut. func getFullName() -> String { return "Waaaf \(firstName) ! WoofWoof \(lastName)" @@ -568,7 +568,7 @@ Prenons un exemple avec `Equatable`. `Equatable` est un protocole qui permet de > **:information_source:** Littéralement `Equatable` signifie qui peut être égal. -Il y a donc des centaines de types qui implémentent ce protocole comme String, Int, Double, et [beaucoup beaucoup d'autres](https://developer.apple.com/documentation/swift/equatable#relationships). +Il y a donc des centaines de types qui implémentent ce protocole comme String, Int, Double, et [beaucoup beaucoup d'autres](https://developer.apple.com/documentation/swift/equatable#relationships). > **:information_source:** La fonctionnalité de pouvoir être égal à quelqu'un a donc été mis en boîte dans un protocole adopté ensuite par des centaines de types. @@ -578,7 +578,7 @@ Je vous propose d'essayer d'implémenter ce protocole pour notre classe `Human`. class Human: Nameable, Equatable { (...) } ``` -Ensuite, le Playground affiche une erreur, car on ne se conforme pas au protocole. +Ensuite, le Playground affiche une erreur, car on ne se conforme pas au protocole. ![](Images/P1/P1C4_3.png) @@ -591,8 +591,8 @@ static func == (lhs: Human, rhs: Human) -> Bool { Cette fonction prend deux paramètres du type avec lequel on travaille, en l'occurrence `Human`. Ce sont les deux valeurs que nous essayons de comparer : -- `lhs` (*left hand side*) : la valeur à gauche du `==` -- `rhs` (*right hand side*) : la valeur à droite du `==` +- `lhs` (*left hand side*) : la valeur à gauche du `==` +- `rhs` (*right hand side*) : la valeur à droite du `==` Et la fonction renvoie un booléen, résultat de l'égalité. @@ -625,4 +625,4 @@ Grâce aux protocoles, notre classe supporte maintenant la fonctionnalité `==` Maintenant que les protocoles sont, je l'espère, un peu plus clairs pour vous, nous allons nous attaquer à notre application et rajouter une liste ! -Rendez-vous dans la prochaine partie ;) ! \ No newline at end of file +Rendez-vous dans la prochaine partie ;) ! diff --git a/9. Table View/Content/part2.md b/9. Table View/Content/part2.md index e15dda6..73f442d 100755 --- a/9. Table View/Content/part2.md +++ b/9. Table View/Content/part2.md @@ -13,7 +13,7 @@ Et je vous propose de commencer par faire un petit tour du propriétaire histoir > **:information_source:** Si vous n'avez pas encore téléchargé le projet, il est encore temps ! Vous le trouverez [ici](https://s3-eu-west-1.amazonaws.com/static.oc-static.com/prod/courses/files/Parcours+DA+iOS/Cours+9+-+Table+View/Whishmas+-+Starter.zip). #### Découverte du projet -Le projet est divisé en 5 fichiers organisés bien évidemment selon le modèle MVC. +Le projet est divisé en 5 fichiers organisés bien évidemment selon le modèle MVC. ![](Images/P2/P2C1_1.png) @@ -43,13 +43,13 @@ class ToyService { toys.append(toy) } } -``` +``` Cette classe utilise le singleton pattern comme on peut le voir dans les deux premières lignes. > **:information_source:** Besoin d'un rappel sur le pattern singleton ? C'est par [ici](https://openclassrooms.com/courses/faites-des-appels-reseaux-dans-votre-application-ios/gerez-les-requetes-concurrentes-avec-le-singleton-pattern). -Ensuite on définit une variable `toys` qui est un tableau de `Toy`. +Ensuite on définit une variable `toys` qui est un tableau de `Toy`. > **:information_source:** Ici, vous remarquerez le mot-clé `private(set)` dont nous n'avons pas encore parlé. Il permet de **laisser public l'accès à la propriété, mais de rendre privé sa modification**. Autrement dit, n'importe qui peut accéder à la propriété `toys`, mais seule la classe `ToyService` peut modifier sa valeur. @@ -80,7 +80,7 @@ Cette classe gère la page qui permet de rajouter un jouet. Il s'agit principale } ``` -> **:information_source:** Il y a aussi une petite extension qui permet de gérer le clavier. Vous avez [un chapitre dédié à la gestion du clavier](https://openclassrooms.com/courses/ajoutez-plusieurs-pages-a-votre-application-ios/gerez-le-clavier) si besoin ;) ! +> **:information_source:** Il y a aussi une petite extension qui permet de gérer le clavier. Vous avez [un chapitre dédié à la gestion du clavier](https://openclassrooms.com/courses/ajoutez-plusieurs-pages-a-votre-application-ios/gerez-le-clavier) si besoin ;) ! ##### ListViewController.swift @@ -102,7 +102,7 @@ Et cet objet mérite un cours, car : - Il est un peu plus complexe qu'un bouton par exemple - Surtout, il y a des Table View partout en iOS -Partout où vous avez une liste qui défile, c'est une Table View qui se cache derrière. +Partout où vous avez une liste qui défile, c'est une Table View qui se cache derrière. Prenons quelques exemples : ![](Images/P2/P2C1_3.png) @@ -120,10 +120,10 @@ Et je vous ai fait un joli schéma que l'on va détailler ensemble : ![](Images/P2/P2C1_5.png) -Une Table View peut-être divisée en **sections**. Ces sections permettent d'organiser les données. +Une Table View peut-être divisée en **sections**. Ces sections permettent d'organiser les données. + +> **:information_source:** Par exemple, dans l'application *Contact*, il y a une section par lettre de l'alphabet. Ou encore dans l'application *Réglages*, les réglages sont découpés en thématiques. -> **:information_source:** Par exemple, dans l'application *Contact*, il y a une section par lettre de l'alphabet. Ou encore dans l'application *Réglages*, les réglages sont découpés en thématiques. - > **:warning:** Le découpage en sections n'est pas obligatoire ! Vous pouvez très bien ne pas utiliser de sections si vous n'en avez pas besoin. C'est ce que fait l'application message, par exemple. Ensuite, au sein d'une section, vous avez trois éléments : @@ -143,9 +143,9 @@ Enfin, la Table View peut également avoir un header et un footer. À ne pas con Enfin, il est important que vous fassiez la distinction entre `row` et `cell` : - `row` représente le numéro de la ligne à laquelle on se trouve dans la section. Elle est de type `Int`. -- `cell` représente la vue, elle est de type `UITableViewCell` qui hérite comme toutes les vues de `UIView`. +- `cell` représente la vue, elle est de type `UITableViewCell` qui hérite comme toutes les vues de `UIView`. -Maintenant qu'on a dit tout ça : je vous remets le schéma, ça ne vous fera pas de mal ;) ! +Maintenant qu'on a dit tout ça : je vous remets le schéma, ça ne vous fera pas de mal ;) ! ![](Images/P2/P2C1_5.png) @@ -157,7 +157,7 @@ Mais vous devez savoir qu'il existe deux styles par défaut, `plain` et `grouped ![](Images/P2/P2C1_6.png) -En général, mais ce n'est pas obligatoire, on utilise le style `plain` pour des **données dynamiques** et `grouped` pour des **données statiques**. +En général, mais ce n'est pas obligatoire, on utilise le style `plain` pour des **données dynamiques** et `grouped` pour des **données statiques**. > **:information_source:** Une liste contient des données dynamiques **si on ne peut pas prévoir à l'avance le contenu exact de la liste, car il est susceptible de varier**. C'est le cas d'une liste de contact ou de messages par exemple. À l'inverse, si on connaît à l'avance le contenu exact d'une liste, on dit qu'elle contient des données statiques. Elles ne bougeront pas. C'est le cas d'une liste de réglages ou d'un menu de navigation par exemple. @@ -175,7 +175,7 @@ Dans le prochain chapitre, nous allons installer notre Table View ! C'est parti ! Nous allons rajouter notre Table View dans le storyboard ! #### Installation de la Table View -Pour cela, ouvrez le fichier Main.storyboard et allez chercher l'objet Table View dans la bibliothèque des objets. +Pour cela, ouvrez le fichier Main.storyboard et allez chercher l'objet Table View dans la bibliothèque des objets. ![](Images/P2/P2C2_1.png) @@ -206,9 +206,9 @@ Je vous laisse jouer avec les attributs `separator` qui concernent la petite lig Pour bien prendre en main la Table View, il faut comprendre comment elle fonctionne. -Comme vous le savez maintenant, une Table View, c'est simplement une liste de cellules organisée éventuellement en sections. Et **cette liste n'a pas de limite de tailles** ! +Comme vous le savez maintenant, une Table View, c'est simplement une liste de cellules organisée éventuellement en sections. Et **cette liste n'a pas de limite de tailles** ! -Et c'est toute la magie des Table View ! Vous pouvez mettre 100 000 éléments dans la liste, ça ne posera pas de problème de performance. Votre iPhone supportera la charge. +Et c'est toute la magie des Table View ! Vous pouvez mettre 100 000 éléments dans la liste, ça ne posera pas de problème de performance. Votre iPhone supportera la charge. Ce petit miracle de performance s'appuie sur une idée très simple. @@ -266,7 +266,7 @@ Or, comme on l'a vu, une liste peut avoir des tonnes de données ! Et on ne va p Du coup, la vue va devoir demander régulièrement au contrôleur de lui donner de nouvelles données. À chaque fois que l'on fait défiler la vue, elle va réclamer de nouvelles données au contrôleur. Le problème, c'est que ma tableView ne sait pas avec quel contrôleur elle va travailler. Ici nous utilisons notre `ListViewController` qui présente une liste de jouets, mais ailleurs nous pourrions en utiliser un autre qui fournit des listes de réglages, ou des listes de contact... - + Pour faire fonctionner notre TableView on doit donc résoudre le problème suivant : 1. Ma tableview doit pouvoir être informée de la composition de la liste par le contrôleur @@ -325,9 +325,9 @@ Ensuite, cette propriété prend pour valeur le contrôleur : ```swift class ViewController: UIViewController { - + var tableView: UITableView - + override func viewDidLoad() { super.viewDidLoad() // J'assigne le contrôleur comme valeur de la propriété dataSource. @@ -354,11 +354,11 @@ class ViewController: UIViewController, UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 1 } - + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 } - + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // (...) } @@ -371,7 +371,7 @@ Vous venez de découvrir un nouveau mode de communication aveugle entre la vue e #### Mise en place du dataSource ! -Mettons toute cette théorie en application dans notre code. +Mettons toute cette théorie en application dans notre code. > **:information_source:** Dans les 4 étapes présentées précédemment, la première est la création d'un protocole et bien sûr, cette étape est déjà faîte par Apple. Donc nous allons commencer directement par la deuxième ! @@ -403,7 +403,7 @@ Lorsque vous faites adopter un protocole à une classe, je vous suggère d'utili class ListViewController: UIViewController {} extension ListViewController: UITableViewDataSource {} -``` +``` > **:information_source:** Besoin d'un rappel, sur les extensions ? C'est [par ici](https://openclassrooms.com/courses/ajoutez-plusieurs-pages-a-votre-application-ios/allez-plus-loin-avec-les-extensions) ! @@ -412,7 +412,7 @@ extension ListViewController: UITableViewDataSource {} Ensuite, nous allons implémenter les méthodes du protocole pour nous y conformer correctement. Nous n'allons en implémenter que trois. Car les autres sont optionnels. > **:information_source:** Eh oui, au passage, sachez que vous pouvez signaler une exigence d'un protocole comme optionnel avec le mot clé optional : -> +> > ```swift > @objc protocol MonProtocole { > optional func maMethodeOptionnelle() @@ -438,7 +438,7 @@ La deuxième méthode se nomme `numbersOfRowsInSection` : ```swift extension ListViewController: UITableViewDataSource { // (...) - + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return ToyService.shared.toys.count } @@ -468,7 +468,7 @@ Nous allons remplir notre Table View en précisant le contenu de nos cellules ! - le nom du jouet - sa marque -Et pour y parvenir, nous devons commencer par rajouter une cellule. +Et pour y parvenir, nous devons commencer par rajouter une cellule. #### Ajouter une cellule dans le storyboard Pour ajouter une cellule, rien de plus simple ! Choisissez `UITableViewCell` dans la bibliothèque des objets : @@ -522,7 +522,7 @@ extension ListViewController: UITableViewDataSource { Cette méthode prend en paramètre un `IndexPath`. `IndexPath` est une structure très simple qui a deux propriétés : -- `section` : contient un numéro de section. +- `section` : contient un numéro de section. - `row` : contient un numéro de ligne. Cette méthode renvoie une `UITableViewCell`. Donc le rôle de cette méthode est de construire la cellule qui va être affichée à la position précisée par `IndexPath`. @@ -543,7 +543,7 @@ Ensuite, nous allons modifier le contenu de cette cellule. Pour cela, il nous fa let toy = ToyService.shared.toys[indexPath.row] ``` -`indexpath.row` donne la ligne à laquelle se trouve la cellule. Ainsi pour chaque ligne de la liste, on va afficher un jouet différent du tableau `toys`. +`indexpath.row` donne la ligne à laquelle se trouve la cellule. Ainsi pour chaque ligne de la liste, on va afficher un jouet différent du tableau `toys`. Il ne nous reste plus qu'à afficher ces données dans la cellule. Pour accéder aux deux labels contenus dans les styles par défaut, nous avons deux propriétés : `textLabel` et `detailTextLabel`. Nous allons modifier le texte de ces deux labels : @@ -594,7 +594,7 @@ override func viewWillAppear(_ animated: Bool) { } ``` -Et voilà ! Notre TableView va maintenant charger ses données à chaque fois que nous revenons sur la page. +Et voilà ! Notre TableView va maintenant charger ses données à chaque fois que nous revenons sur la page. Cette Table View est officiellement fonctionnelle ! Vous pouvez tester en lançant le simulateur et en ajoutant quelques données. @@ -606,6 +606,7 @@ Cette Table View est officiellement fonctionnelle ! Vous pouvez tester en lança - On peut choisir parmi 4 styles par défaut pour les cellules. - On précise le contenu de la cellule avec la méthode `cellForRowAt` du protocole `UITableViewDataSource`. - On lance le chargement des données avec la méthode reloadData de `UITableView`. +- Un delegate est déclaré avec l'indicateur `weak` pour éviter un retain cycle entre l'objet et son delegate. Dans la prochaine partie, nous allons ajouter plusieurs fonctionnalités à notre application : la gestion de plusieurs catégories, la possibilité de supprimer des messages, la création de cellules customisées et bien d'autres ! @@ -615,9 +616,9 @@ Et au passage, vous allez approfondir votre compréhension des Table View et des ### Bonus : Découvrez le concept de références -> **:question:** Hop hop hop, tu voulais pas nous parler d'un truc important encore ?! +> **:question:** Hop hop hop, tu ne voulais pas nous parler d'un truc important encore ?! -Ah si ! Quand je vous ai introduit le protocole `UITableViewDataSource`, on a d'une part limité ce protocol à des classes, en adossant `class` à la déclaration de notre protocole : +Ah si ! Quand je vous ai introduit le protocole `UITableViewDataSource`, on a d'une part limité ce protocole à des classes, en adossant `class` à la déclaration de notre protocole : ```swift protocol UITableViewDataSource: class { @@ -640,6 +641,7 @@ Alors, pourquoi est-ce qu'on a fait tout ça, et qu'est-ce que ça veut dire ? O Dans les languages de programmation un peu modernes (comme Swift!), les objets que vous crééez restent dans la mémoire tant qu'au moins une référence existe vers cet objet. Quand mon objet n'a plus aucun autre objet qui n'a de référence sur lui, pouf ! Il disparaît ! Sous iOS, la technologie qui fait tout ça s'appelle Automatic Reference Counting (ARC). +Dans d'autres languages, on parle de _Garbage collector_ pour désigner cette technologie: le programme nettoie la mémoire en enlevant les objets qui ne servent plus à rien. Et comment sait-on qu'un objet ne sert plus à rien ? Quand il n'a plus de références! Dans ce cas, mon programme n'a plus aucun moyen d'accéder à cet objet, et il est considéré comme **perdu**. **Tout ça ne s'applique qu'aux classes, les structures et les enums ne sont pas concernées.** D'où le petit mot `class` dans la déclaration de notre protocole, qui permet de garantir que seule une classe pourra adopter ce protocole. @@ -680,15 +682,39 @@ Du coup en terme de réference, quand j'écris après dans mon viewController `t > **:information_source:** Et là, c'est le drame. 😱😱😱 > **:question:** Pourquoi c'est le drame ? - -Parce que sans faire attention, j'ai créé un **retain cycle**. En fait chaque objet a une référence vers l'autre. Même si mon View Controller n'est plus dans la navigation, et qu'aucun objet n'a de référence vers lui, le couple View Controller <> Table View ne disparaitrat jamais. C'est ce qu'on appelle aussi une _fuite mémoire_. + +Parce que sans faire attention, j'ai créé un **retain cycle**. En fait chaque objet a une référence vers l'autre. Même si mon View Controller n'est plus dans la navigation, et qu'aucun objet n'a de référence vers lui, le couple View Controller <> Table View ne disparaitrat jamais. Ce qui créé au final une _fuite mémoire_. > **:question:** Mon Dieu, mais qu'est-ce qu'on va faire ??! -Pas de panique ! Vous l'aurez sans doute compris, c'est là que le mot `weak` entre en jeu ! Weak veut dire: cette propriété me permet d'accéder à mon objet, mais ne compte pas de référence dessus. Si on reprend notre schéma: +Pas de panique ! Vous l'aurez sans doute compris, c'est là que le mot `weak` entre en jeu ! +Pour comprendre `weak`, on va d'abord regarder ce que fait son contraire: `strong`. + +Par défaut, quand je déclare une propriété sur un objet de type `Objet`: `var monObjet: Objet`, c'est en fait équivalent à écrire: `strong var monObjet: Objet`. Ma référence vers mon instance de `Objet` doit être **forte** pour maintenir mon objet dans la mémoire. Quand le nombre de référence forte vers mon objet tombe à zéro, il n'y a plus rien pour le garder dans la mémoire et c'est là qu'il disparaît. + +Du coup, quand je prépose `weak` à la déclaration de ma variable, j'indique que je veux une référence faible. Et au contraire d'une référence forte, une référence faible ne retient pas mon objet dans la mémoire ! Je peux accéder à mon objet dans la mémoire, mais ce n'est pas moi qui le retiendrai je ne vais pas le retenir s'il doit disparaître. + +Si on reprend notre schéma de tout à l'heure, avec la déclaration en weak, ça donne ça: ![](Images/P2/P2C4_9.png) -Et voilà le travail, le mot weak permet de briser ce fameux **retain cycle**, parce que par défaut une propriété est **strong** : ma propriété maintient un lien fort avec l'objet: elle compte comme une référence. +Et voilà le travail, le mot weak permet de briser ce fameux **retain cycle** ! Quand mon contrôleur ne sera plus dans la navigation, et qu'il n'aura plus de référence vers lui, ma `tableView` ne l'empêchera pas d'être enlevé de la mémoire. + +> **:question:** Ouais, mais on me la fait pas, ton schéma est faux, nous on a déclaré notre tableView avec un IBOutlet et avec le mot clef `weak`. + +C'est vrai, quand on déclare un Outlet avec `weak`, en théorie notre objet ne devrait même pas pouvoir rester dans la mémoire puisqu'il n'y a aucune référence `strong` pour le retenir, non ? +Bien sûr, vous savez que c'est faux, puisque sinon nos apps ne fonctionneraient pas depuis le début héhé. +Alors, qu'est-ce qu'il se passe au juste en réalité ? Et bien pour ça, il suffit de se souvenir, quand tout bon `UIViewController` gère une vue. Et si on reprend notre schéma, dans la réalité, on a ça: + +![](Images/P2/P2C4_10.png) + +La vue maintient un lien fort sur l'ensemble de ses sous-vues. Et tant que mon contrôleur est présent, il maintient aussi un lien sur sa vue, donc ma `tableView` reste bien dans la mémoire. On déclare nos outlets en `weak` pour éviter une redondance, ou pour éviter des problèmes si on créé des liens entre objets qui n'ont rien à voir! Depuis le temps qu'on déclare des outlets, ça devait vous démanger de ne pas savoir non ? :) Si tout ça vous paraît compliqué, pas de panique. Retenez simplement le concept de retain cycle, et que si deux objets s'auto-référencent, vous allez avoir des problèmes de mémoire. Lorsque vous créérez vos propres delegates, pensez à les indiquer en `weak` pour éviter ce problème, et tout ira bien ! + +#### En résumé +- Un objet reste dans la mémoire tant qu'au moins une référence **forte** existe vers cet objet. +- Si deux objets s'auto-référencent avec des références fortes, cela créé un **retain cycle**: mes objets ne peuvent plus être nettoyés de la mémoire. +- le mot réservé `weak` permet de résoudre ce problème: les références dites **faibles** ne comptent pas lorsque le programme détermine si l'objet est encore utile ou pas. +- On déclare un `delegate` en `weak` pour éviter de créer un cycle de rétention et du coup une fuite mémoire +- Les propriétés avec des IBOutlets sont déclarées en `weak`: en général l'objet aura déjà une référence interne et en rajouter une autre ne servira pas.