Objective-C succinctement Protocoles

En Objective-C, un protocole est un groupe de méthodes pouvant être implémentées par n'importe quelle classe. Les protocoles sont essentiellement les mêmes que les interfaces en C # et les deux ont des objectifs similaires. Ils peuvent être utilisés en tant que type de pseudo-données, ce qui est utile pour s'assurer qu'un objet typé dynamiquement peut répondre à un certain ensemble de messages. Et, comme toute classe peut "adopter" un protocole, elle peut être utilisée pour représenter une API partagée entre des classes totalement non liées..

La documentation officielle décrit à la fois une méthode informelle et une méthode formelle pour déclarer des protocoles, mais les protocoles informels ne sont en réalité qu'une utilisation unique de catégories et n'offrent pas autant d'avantages que les protocoles formels. Dans cet esprit, ce chapitre se concentre uniquement sur formel les protocoles.


Créer un protocole

Voyons d’abord comment déclarer un protocole formel. Créez un nouveau fichier dans Xcode et sélectionnez l’icône de protocole Objective-C sous Mac OS X> Cacao:

Icône Xcode pour les fichiers de protocole

Comme d'habitude, cela vous demandera un nom. Notre protocole contiendra des méthodes pour calculer les coordonnées d'un objet, appelons-le CoordinateSupport:

Nommer le protocole

Cliquez sur Suivant et choisissez l'emplacement par défaut pour le fichier. Cela créera un protocole vide qui ressemble presque exactement à une interface:

// CoordinateSupport.h #import  @protocol CoordinateSupport  @fin

Bien sûr, au lieu de @interface directive, il utilise @protocole, suivi du nom du protocole. le la syntaxe nous permet d'intégrer un autre protocole dans CoordinateSupport. Dans ce cas, nous disons que CoordinateSupport comprend également toutes les méthodes déclarées dans la NSObject protocole (à ne pas confondre avec le NSObject classe).

Ensuite, ajoutons quelques méthodes et propriétés au protocole. Cela fonctionne de la même manière que la déclaration de méthodes et de propriétés dans une interface:

#importation  @protocol CoordinateSupport  @propriété double x; @propriété double y; @propriété double z; - (NSArray *) arrayFromPosition; - (double) magnitude; @fin

Adopter un protocole

Toute classe qui adopte ce protocole est assurée de synthétiser le X, y, et z propriétés et mettre en œuvre le arrayFromPosition et ordre de grandeur méthodes. Alors que cela ne dit pas Comment ils seront implémentés, cela vous donne la possibilité de définir une API partagée pour un ensemble arbitraire de classes.

Par exemple, si on veut les deux Navire et La personne pour pouvoir réagir à ces propriétés et méthodes, nous pouvons leur dire d’adopter le protocole en le plaçant entre crochets après la déclaration de superclasse. Notez également que, tout comme pour l’utilisation d’une autre classe, vous devez importer le fichier de protocole avant de l’utiliser:

#importation  #import "CoordinateSupport.h" @interface Person: NSObject  @property (copy) NSString * name; @property (strong) NSMutableSet * friends; - (vide) dites bonjour; - (vide) dites au revoir; @fin

Maintenant, en plus des propriétés et méthodes définies dans cette interface, le La personne il est garanti que la classe répondra à l'API définie par CoordinateSupport. Xcode vous avertira que le La personne la mise en œuvre est incomplète jusqu'à ce que vous synthétisez X, y, et z, et mettre en œuvre arrayFromPosition et ordre de grandeur:

Avertissement d'implémentation incomplète pour Personne

De même, une catégorie peut adopter un protocole en l'ajoutant après la catégorie. Par exemple, pour dire au La personne classe à adopter le CoordinateSupport protocole dans le Rapports catégorie, vous utiliseriez la ligne suivante:

@interface Person (Relations) 

Et, si votre classe doit adopter plusieurs protocoles, vous pouvez les séparer par des virgules:

@interface Person: NSObject 

Avantages des protocoles

Sans protocoles, nous aurions deux options pour assurer à la fois Navire et La personne implémenté cette API partagée:

  1. Re-déclarer exactement les mêmes propriétés et méthodes dans les deux interfaces.
  2. Définir l'API dans une superclasse abstraite et définir Navire et La personne en tant que sous-classes.

Aucune de ces options n'est particulièrement attrayante: la première est redondante et sujette aux erreurs humaines, et la seconde est très restrictive, en particulier si elles héritent déjà de différentes classes parentes. Il doit être clair que les protocoles sont beaucoup plus souples et réutilisables, car ils évitent à l'API de dépendre d'une classe particulière..

Le fait que tout La classe peut facilement adopter un protocole permet de définir des relations horizontales au-dessus d'une hiérarchie de classe existante:

Liaison de classes non liées à l'aide d'un protocole

En raison de la nature flexible des protocoles, les divers frameworks iOS en font bon usage. Par exemple, les contrôles de l'interface utilisateur sont souvent configurés à l'aide du modèle de conception de délégation, dans lequel un objet délégué est responsable de la réaction aux actions de l'utilisateur. Au lieu d'encapsuler les responsabilités d'un délégué dans une classe abstraite et de le forcer à le sous-classer, iOS définit l'API nécessaire au délégué dans un protocole. De cette façon, il est incroyablement facile pour tout objet pour agir en tant qu'objet délégué. Nous allons explorer cela plus en détail dans la seconde moitié de cette série, iOS succinctement.


Protocoles en tant que pseudo-types

Les protocoles peuvent être utilisés en tant que types psuedo-data. Au lieu de s'assurer qu'une variable est une instance d'une classe, l'utilisation d'un protocole en tant qu'outil de vérification de type garantit que la variable est toujours conforme à une API arbitraire. Par exemple, le suivant la personne variable est garanti pour implémenter l'API CoordinateSupport.

La personne  * personne = [[Person alloc] init];

Néanmoins, l’application forcée du protocole est souvent plus utile lorsqu’elle est utilisée avec le identifiant Type de données. Cela vous permet d'assumer certaines méthodes et propriétés tout en ignorant complètement la classe de l'objet.

Et bien sûr, la même syntaxe peut être utilisée avec un paramètre de méthode. L'extrait suivant ajoute une nouvelle getDistanceFromObject: méthode à l'API dont le paramètre est requis pour se conformer à CoordinateSupport protocole:

// CoordinateSupport.h #import  @protocol CoordinateSupport  @propriété double x; @propriété double y; @propriété double z; - (NSArray *) arrayFromPosition; - (double) magnitude; - (double) getDistanceFromObject: (id )L'object; @fin

Notez qu'il est tout à fait possible d'utiliser un protocole dans le même fichier que celui défini.

Contrôle de conformité dynamique

En plus de la vérification de type statique décrite dans la dernière section, vous pouvez également utiliser le conforme au protocole: méthode définie par le NSObject protocole pour vérifier dynamiquement si un objet est conforme à un protocole ou non. Ceci est utile pour éviter les erreurs lorsqu’on travaille avec des objets dynamiques (objets saisis comme identifiant).

L'exemple suivant suppose que La personne la classe adopte le CoordinateSupport protocole, tandis que le Navire la classe n'a pas. Il utilise un objet typé dynamiquement appelé mysteryObject pour stocker une instance de La personne,et utilise ensuite conforme au protocole: pour vérifier s'il dispose d'un support de coordonnées. Si c'est le cas, vous pouvez utiliser le X, y, et z propriétés, ainsi que les autres méthodes déclarées dans la CoordinateSupport protocole:

// main.m #import  #import "Person.h" #import "Ship.h" int main (int argc, const char * argv []) @autoreleasepool id mysteryObject = [[Person alloc] init]; [mysteryObject setX: 10.0]; [mysteryObject setY: 0.0]; [mysteryObject setZ: 7.5]; // Ne commentez pas la ligne suivante pour voir la partie "else" du conditionnel. // mysteryObject = [[Ship alloc] init]; if ([mysteryObject conformeToProtocol: @protocol (CoordinateSupport)]]) NSLog (@ "Ok pour supposer un support de coordonnées."); NSLog (@ "L'objet est situé à (% 0.2f,% 0.2f,% 0.2f)", [mysteryObject x], [mysteryObject y], [mysteryObject z]);  else NSLog (@ "Erreur: Impossible d'assumer un support de coordonnées."); NSLog (@ "Je ne sais pas du tout où se trouve cet objet…");  retourne 0; 

Si vous décommentez la ligne qui réattribue le mysteryObject à un Navire par exemple, le conforme au protocole: la méthode retournera NON, et vous ne pourrez pas utiliser en toute sécurité l’API définie par CoordinateSupport. Si vous n'êtes pas sûr du type d'objet qu'une variable va contenir, ce type de vérification de protocole dynamique est important pour empêcher le blocage de votre programme lorsque vous essayez d'appeler une méthode qui n'existe pas..

Notez également le nouveau @protocole() directif. Cela fonctionne beaucoup comme @sélecteur(), sauf qu'au lieu d'un nom de méthode, il faut un nom de protocole. Il retourne un Protocole objet, qui peut être passé à conforme au protocole:, parmi d'autres méthodes intégrées. Le fichier d’en-tête de protocole ne ne pas doivent être importés pour @protocole() travailler.


Protocoles de déclaration anticipée

Si vous travaillez avec de nombreux protocoles, vous vous retrouverez dans une situation où deux protocoles s'appuient l'un sur l'autre. Cette relation circulaire pose un problème au compilateur, car il ne peut pas importer l'un avec l'autre sans l'autre. Par exemple, supposons que nous essayons d’abréger certaines fonctionnalités du GPS dans un GPSSupport protocole, mais veulent pouvoir convertir entre les coordonnées "normales" de notre existant CoordinateSupport et les coordonnées utilisées par GPSSupport. le GPSSupport le protocole est assez simple:

#importation  #import "CoordinateSupport.h" @protocol GPSSupport  - (void) copyCoordinatesFromObject: (id )L'object; @fin

Cela ne pose aucun problème, c’est-à-dire jusqu’à ce que nous devions faire référence à GPSSupport protocole de CoordinateSupport.h:

#importation  #import "GPSSupport.h" @protocol CoordinateSupport  @propriété double x; @propriété double y; @propriété double z; - (NSArray *) arrayFromPosition; - (double) magnitude; - (double) getDistanceFromObject: (id )L'object; - (void) copyGPSCoordinatesFromObject: (id )L'object; @fin

Maintenant le CoordinateSupport.h le fichier nécessite le GPSSupport.h fichier à compiler correctement, et vice versa. C'est un problème de type poule ou œuf, et le compilateur ne l'aimera pas beaucoup:

Erreur du compilateur causée par des références de protocole circulaires

Résoudre la relation récursive est simple. Tout ce que vous avez à faire est de déclarer en aval l'un des protocoles au lieu d'essayer de l'importer directement:

#importation  @protocol CoordinateSupport; @protocol GPSSupport  - (void) copyCoordinatesFromObject: (id )L'object; @fin

Tout @protocol CoordinateSupport; dit est-ce CoordinateSupport est en effet un protocole et le compilateur peut supposer qu’il existe sans l’importer. Notez le point-virgule à la fin de l'instruction. Cela pourrait être fait dans l'un des deux protocoles; le but est de supprimer la référence circulaire. Le compilateur ne se soucie pas de savoir comment vous le faites.


Résumé

Les protocoles sont une fonctionnalité incroyablement puissante d'Objective-C. Ils vous permettent de capturer des relations entre des classes arbitraires lorsqu'il n'est pas possible de les connecter à une classe parent commune. Nous utiliserons plusieurs protocoles intégrés dans iOS succinctement, autant de fonctions de base d'une application iPhone ou iPad sont définies en tant que protocoles.

Le chapitre suivant présente les exceptions et les erreurs, deux outils très importants pour la gestion des problèmes inévitables lors de l’écriture de programmes Objective-C..

Cette leçon représente un chapitre de Objective-C Succinctly, un eBook gratuit de l’équipe de Syncfusion..