À Propos des « Protocoles » iOS

Lors d’un entretien récent il m’a été posé la question suivante :
« qu’est-ce qu’un Protocole ? »

La question m’a un peu désemparé puisqu’il s’agit d’un des tout premiers concepts que l’on apprend en Cocoa (vous sauriez expliquer par exemple ce qu’est un PPCM ? (1))

Je donne donc une réponse improvisée, en l’occurrence approximative, et on me répond que
« c’est un contrat, c’est à dire un ensemble de fonctions que vous vous engagez à implémenter »

C’est une bonne définition, néanmoins la réalité est un peu plus subtile.

Un protocole (concept Objective-C et non iOS) définit pour un développeur qui l’utilise pour sa classe, un ensemble de fonctions (qu’il devra écrire), qui seront appelées dans une situation particulière (exemple : on touche l’écran).

Je ne suis pas tout à fait d’accord avec le terme de « contrat ». D’abord ne pas implémenter ces fonctions ne crée aucun problème de compilation, ne génèrera aucun avertissement (« warning »), votre application plantera probablement à l’exécution mais ce n’est pas garanti ! (2)(3). Bien évidemment une telle situation est fortement déconseillée, mais je pars du principe que tout bon développeur doit s’efforcer de corriger la totalité des warnings (4).

Observons de plus près les Protocoles, la définition qu’en donne Apple n’est pas que vous devez implémenter ces fonction, mais que le système s’attend à ce que vous l’ayez fait. Quelle est la différence ?

1) la définition d’un Protocole admet deux mots clés : @required (marquage par défaut) et @optional.
Par exemple si vous gérez une UITableView par code (utilisation de UITableViewDelegate), alors vous « devez » implémenter numberOfRowsInSection (en réalité vous êtes « supposé » l’avoir fait). Si vous voulez que les hauteurs de ligne soient différentes selon les données à afficher vous « pouvez » implémenter heightForRowAtIndexPath:. Vous avez ce choix.
Techniquement, lorsque la classe UITableView veut appeler une fonction marquée @optional, elle vérifie avant, si elle a été implémentée (au moyen de respondsToSelector:), mais pas lorsqu’elle est @required, ce qui lèvera immédiatement une exception à l’exécution le cas échéant.

2) cas d’une fonction marquée @required non implémenté qui ne crashe pas : si votre table est vide (vous écrivez numberOfSectionsInTableView: et lui faites renvoyer zéro) votre application ne crashera jamais car numberOfRowsInSection: ne sera jamais appelée ! Vous pouvez vous trouvez dans cette situation si vous avez prévu une feature complexe qui ne sera rendue accessible que dans une version ultérieure (vous aurez alors pris soin d’implémenter des mécanismes générant un warning, une fenêtre d’alerte en Debug, ou mieux, une erreur de compilation(5)).

3) si vous implémentez vous même votre protocole, vous savez que certaines fonction ne seront jamais appelées pour la version en cours de développement. Même précautions qu’en 2)

—–
(1) On utilise pourtant régulièrement les PPCM, par exemple si vous partagez deux pizzas en trois portions vous ne pourrez obtenir qu’il n’y ait pas de part aussi petite que 2/6, soit un tiers.
(2) Dans le cas d’un crash vous pourrez trouver dans le log quelque chose comme «  …numberOfRowsInSection:]: unrecognized selector sent to instance … » signifiant que vous auriez dû implémenter numberOfRowsInSection: !
(3) pourquoi est-il très important de savoir que votre code peut crasher parfois, plutôt que systématiquement (TODO)(prendre la cas de new + article séparé)
(4) supprimer totalement les warnings ? (TODO)(type de warnings et leurs conséquences, activer de nouveaux warnings, ..)
(5) transformer une erreur d’exécution en erreur de compilation (TODO)(expliquer pourquoi)(cas triviaux + cet exemple)(exemple d’une lib dont des paramètres ont changé)

Social tagging: >

Laisser un commentaire