LogicielMac.com
  • twitter
  • facebook
  • Accueil
  • Forum
  • Logiciels
    • Internet
    • Pratique
    • Utilitaire
    • Multimédia
    • Sécurité
    • Jeux
  • Rubriques
    • Actualités
    • Screencast
    • Articles
  • Espace développeurs
  • Boutique
LogicielMac.com
  • Accueil
  • Forum
  • Logiciels
    • Internet
    • Pratique
    • Utilitaire
    • Multimédia
    • Sécurité
    • Jeux
  • Rubriques
    • Actualités
    • Screencast
    • Articles
  • Espace développeurs
LogicielMac.com
  • Accueil
  • Forum
  • Logiciels
    • Internet
    • Pratique
    • Utilitaire
    • Multimédia
    • Sécurité
    • Jeux
  • Rubriques
    • Actualités
    • Screencast
    • Articles
  • Espace développeurs
  • Boutique
  • Accueil
  • News
  • Logiciels / Applications
  • La Programmation : Le Langage Objective-C (2)
Actualités
Tweeter
La Programmation : Le Langage Objective-C (2)

La Programmation : Le Langage Objective-C (2)

Actualité rédigée par le 16/01/2007 à 23:56

La Programmation : Le Langage Objective-C (2)

 

Sujets abordés :

  • Utilisation d'une classe
  • Héritage
  • Les Protocoles
  • Les Catégories

Bonjour à tous et bienvenue dans ce nouveau tutoriel sur la programmation en Objective-C avec l'API Cocoa d'Apple. Tout d'abord merci à tous pour vos encouragements, ça fait plaisir et ça donne envie de continuer . Je vous invite à revoir l'épisode précédent afin d'y prendre en compte les modifications que j'y ai apporté, pour y accéder, vous passez par la section "tutoriaux" dans la partie "La Programmation".

Je suis désolé pour l'attente de quelques mois que je vous ai faite subir, mais je vais chercher à être plus rapide maintenant dans l'écriture de ce tutoriel. Aujourd'hui, nous allons nous attaquer à l'utilisation d'une classe, ainsi que la gestion de la mémoire mais aussi et surtout au concept d'héritage, concept majeur de la POO et donc de l'Objective-C !

 

 

La Classe Personnel

 

Utilisation de notre classe

Alors, pour l'instant notre classe n'est pas très volumineuse, il n'y a pas grand chose dedans ! Mais bon on peut toujours en faire quelque chose. Je précise que je ne ferais pas d'interaction avec l'utilisateur ici (en utilisant printf/scanf), tout sera programmé en dur dans le code, les interactions n'apportent en effet pas grand chose en l'occurrence, du moment que vous connaissez le code et que vous voyez le résultat.

Notre programme se basera donc essentiellement sur la fonction C NSLog() . Et nous allons commencer par créer 3 nouveaux personnels pour vous montrer comment on utilise notre petite classe ! En implémentant vos méthodes, vous avez déjà eu l'occasion de voir ce système, seulement là, nous allons utiliser nos méthodes à nous, vous verrez que rien ne change. Pour commencer, il faut retourner dans votre fichier principal, il porte le nom de votre projet et contient la fonction main(). Chez moi, ce fichier se nomme "Tutoriel_CLU.m". Déjà, vous pouvez effacer tout ce qui se trouve entre "NSAutoreleasePool..." et "[pool release] " mais n'effacez pas ces balises, nous y reviendrons plus tard. Entre elles vous pouvez écrire tout ça :

Personnel *p1 = [[Personnel alloc] initWithNom:@"Psycho"
andNumTel:@"000444"];

Personnel *p2 = [Personnel personnelWithNom:@"Tom"
andNumTel:@"3554"];

id p3 = [Personnel personnelWithNom:@"Zeus"];

NSLog(@"\nListe :\n%@\n%@\n%@\n",p1,p2,p3);

[p3 setNumTel:[p1 numTel]];

NSLog(@"\nNom de p2 : %@\np3 :\n%@\n",[p2 nomPers],p3);

[p1 release];
[p2 release];
[p3 release];

Voilà, voilà... Nous commençons notre petit programme par une série de déclaration / allocation / initialisation d'objets. Dans le premier cas nous déclarons un pointeur sur un Personnel, il ne faut pas oublier qu'il faut toujours déclarer des pointeurs sur objet ! Donc nous déclarons p1 de type "Personnel *" et puis nous l'allouons et l'initialisons à l'aide de notre méthode désignée qu'on a vu dans le précédent chapitre. Nous faisons de même pour p2 en utilisant cette fois-ci notre méthode de classe qui fait, je vous le rappelle, l'allocation et l'initialisation elle-même, dans tous les cas cette instruction a le même effet que la précédente (aux arguments près) et nous avons ainsi deux nouveaux "Personnel" ayant chacun des valeurs différentes.

Dans le cas de la troisième instruction, vous remarquerez que j'ai remplacé "Personnel *" par le mot clé "id". Ce mot clé représente en fait un pointeur sur un objet, d'où l'absence de l'étoile, sur un NSObject, c'est-à-dire un objet sans aucune autre spécificité que d'être un objet. Ce mot clé permet de faire du "typage dynamique", il s'agit d'un concept important de l'Objective-C et qui permet de moins s'embêter avec toutes les classes, en fait, on ne précise pas au compilateur quelle classe d'objet sera affectée à cette valeur et ce ne sera que lorsque l'application tournera que le type de l'objet sera connu. Si vous voulez, vous pouvez déclarer des "id" partout sans distinction, à chaque fois que vous avez besoin d'un objet, vous mettez un "id", le problème c'est que vous ne donnez aucune information au compilateur et que si vous envoyez à cet objet un message auquel il n'est pas capable de répondre, vous n'obtiendrez pas le résultat souhaité ce qui peut être dommageable. Si vous êtes sûr du type d'objet que vous souhaitez affecté à votre variable, je ne vous conseillerais que trop de le préciser.

Toujours est-il qu'on affecte à notre variable de type "id" un "Personnel" grâce à notre méthode de classe
"personnelWithName: ", donc lorsque l'application tournera, cette objet sera un Personnel.

Dans l'instruction qui suit, on utilise la fonction C "NSLog() " qui nous permet d'afficher un message sur la console, en fait cette fonction rédige un "rapport" d'erreur (fichier log) et nous ne faisons que l'afficher dans la console afin de voir l'état de nos objets. Quand vous exécutez le programme vous obtenez la liste des personnels avec leur nom et numéro de téléphone, dans le cas de Zeus ce dernier est absent.

Justement, dans l'instruction suivante nous utilisons la méthode "setNumTel:", qui nous permet d'affecter un numéro de téléphone à notre objet, et nous donnons comme paramètre une autre méthode ! C'est tout à fait normal, puisque cette méthode "numTel", renvoie une "NSString" et c'est ce dont nous avons besoin dans notre méthode "setNumTel:", ainsi "p3" aura le même numéro de téléphone que "p1". Et pour finir, nous faisons un nouvel affichage pour contrôler que le changement a bien eu lieu.

 

Héritage

Voilà on peut entamer l'héritage, concept très important de l'Objective-C et de la programmation objet en général. L'héritage est le fait de mettre en relation 2 classes, mais d'une façon spécifique, la relation entre super-classe et sous-classe est de type "est un". Prenons un exemple dans la nature. La biologie a cette avantage de présenter un nombre incroyable de classification et sous-classification. On va donc reprendre l'exemple de notre Chat, comme tout le monde le sait, enfin j'espère, le Chat "est un" Félin, tout comme le Tigre ou le Lion, on déjà un début d'arborescence : la racine c'est "Félin" est on a nos branches : "Chat", "Lion", "Tigre". Chacun de ces animaux sont des Félins, c'est-à-dire qu'ils ont des caractères communs tels que la forme du crâne, les pupilles en amande, les griffes rétractiles, etc. Mais bien qu'ils soient de la même famille, on ne peut pas dire que ces Félins sont pareils, ils ont des caractères propres, par exemple le Tigre a le pelage rayé, le Lion a une crinière et le chat miaule et ronronne. Donc l'héritage c'est ça, c'est "factoriser", autrement dit réunir, du code commun à plusieurs classes afin d'en économiser la réécriture, qui peut être fastidieuse à force ! Si on en revient à nos félins, on peut dire sans peine que ce sont des mammifères tout comme les canidés ou les primates, et parmi ces primates on compte les chimpanzés, les singes et les humains. Tout comme le chat, les primates on des mamelles et des poils puisqu'ils sont mammifères, en revanche ce qui les différencient des chats c'est le fait qu'ils ont des ongles et non pas des griffes, ou bien des yeux avec pupille ronde et non en amande. Et puis la différence entre singe et humain c'est qu'on marche généralement sur deux pattes plutôt que 4, etc.

Donc en programmation c'est exactement le même principe, la super-classe permet de partager du code entre ses sous-classes pour ne pas avoir à le réécrire, chacune de ces sous-classes peut être instanciée comme étant un objet de sa super-classe, etc. Mais ne vous inquiétez pas nous allons y revenir. Déjà, ce qu'il faut savoir c'est qu'en Objective-C, comme en Java, l'héritage est simple, c'est-à-dire qu'une classe ne peut avoir qu'une et une seule super-classe, en revanche cette super-classe peut avoir une infinité de sous-classe, on peut remarquer aussi que toutes les classes héritent directement ou indirectement de la classe racine (root class) NSObject, en Java il s'agit de la classe Object, c'est-à-dire que toute classe est un objet et hérite de méthode tel que l'allocation, désallocation, description, release, etc. C'est une grosse différence avec le langage C++ qui lui possède un héritage multiple et pas de super-classe racine cela pose des problèmes lorsqu'il faut par exemple créer des méthodes d'affichage, on est obligés d'utiliser un concept obscure nommé l'amitié... En revanche il peut être utile de donner à des classes différentes dans des branches différentes (c'est-à-dire qui héritent de super-classes différentes) sans aucun caractère en commun, et bien justement, des caractères commun, le Java et l'Objective-C permettent de parer à ce problème grâce au système des interfaces (pour Java) et des protocoles (pour Objective-C). Si une classe ne peut hériter que d'une super-classe, en revanche elle peut hériter d'une infinité de protocole. Mais on parlera de ça plus loin de ça, ça peut-être utile dans de grosses arborescences.

 

Alors je vous le dis tout de suite, je suis assez flemmard mais aussi l'écriture de ce qui suit dans le programme est plutôt fastidieuse, alors au lieu de vous écrire tout ce qu'il faut mettre je vais vous donner les fichiers à télécharger pour les ajouter le plus simplement du monde à votre projet. Nous allons y ajouter 3 nouvelles classes pour vous exposer concrètement la notion d'héritage. Voici donc, un petit diagramme de classes généré à l'aide d'Xcode :

Ceci est un diagramme UML plus ou moins formalisé. Dans le cartouche du rectangle vous avez le nom de la classe, puis en-dessous vous avez deux espaces, nommés ici : Properties (propriétés) et indiquant tous les attributs de votre classe suivi de leur type, puis le champ Operations (opérations) contenant toutes les méthodes de votre classe, les méthodes non-soulignées sont les méthodes d'instance et les méthodes soulignées sont les méthodes de classe. Ce que ce diagramme nous montre de très intéressant, ce sont les flèches, les flèches qui indiquent quelle classe hérite de quelle classe, en l'occurrence 3 des classes héritent de Personnel, directement ou indirectement, et Personnel hérite de NSObject qui n'est pas indiquée ici.

Maintenant, je vous propose de télécharger ce petit fichier au format ".SITX" : tutoriel.sitx pour télécharger appuyez sur la touche "alt" en cliquant sur le lien.

Le dossier contient 10 fichiers :

  1. En-Tete.h : Il permettra de simplifier l'importation de tous les fichiers d'en-tête de classes.
  2. Personnel.h : Fichier d'en-tête de la classe Personnel
  3. Personnel.m : Fichier source de la classe Personnel
  4. Employe.h : Fichier d'en-tête de la classe Employe
  5. Employe.m : Fichier source de la classe Employe
  6. Directeur.h : Fichier d'en-tête de la classe Directeur
  7. Directeur.m : Fichier source de la classe Directeur
  8. Commercial.h : Fichier d'en-tête de la classe Commercial
  9. Commercial.m : Fichier source de la classe Commercial
  10. main.m : Contient un petit programme utilisant les classes.

Contrairement à ce qui serait souhaitable, aucun de ces fichiers n'est commenté ! Mais bon en l'occurrence ce n'est pas grave puisque je vais tout vous expliquer en détail dans ce tutoriel.

Pour commencer, le premier fichier n'est pas particulièrement compliqué, d'ailleurs il ne s'agit pas d'une classe mais juste d'un fichier permettant de réunir les "#import" à faire dans le fichier source principal. Donc au début du fichier ".m" contenant la fonction "main()" je remplace tous les "#import" par un simple : "#import "En-Tete.h"" ainsi je ne surcharge pas le code.

Alors voilà, maintenant on passe aux deux fichiers suivants concernant notre classe "Personnel". Cette petite classe a subit pas mal de modifications depuis la diffusion du premier épisode à cause de (ou grâce à???) mes diverses recherches sur l'Objective-C qui m'ont permises d'améliorer son code.

Vous pouvez remarquer l'apparition de la méthode de classe "+ (void) initialize; ", cette petite méthode existant déjà dans la classe NSObject permet d'initialiser la classe, c'est-à-dire d'initialiser des valeurs, de mettre en place différentes variable... enfin tout ce qui doit être fait à la création de votre classe. Cette méthode n'est pas forcément appelée, en fait elle est appelée juste avant l'envoie du premier message à votre classe. Vous avez aussi une nouvelle méthode : "calculPaie:" dont on va reparler plus tard, et qui va encore un peu bouger à l'avenir. Voilà pour le fichier d'en-tête.

En ce qui concerne le fichier source, la première modification est la variable " static", il s'agit de notre variable de classe... écrite ainsi dans l'implémentation de la classe (entre les bornes @implementation et @end elle devient alors une variable privée (ce n'est d'ailleurs pas négociable ) les objets partageront la même variable avec la même valeur. Ici, nous voyons qu'elle n'est pas initialisée c'est quelque chose que nous allons faire dans notre fameuse méthode
"initialize". Mais revenons-y ! Je vous ai dit que cette méthode est appelée juste avant le premier message envoyé à un objet de votre classe (ou à la classe elle-même), mais les super-classes de votre méthode doivent être elles aussi initialisées. Il n'y a pas à s'inquiéter, avant d'initialiser votre classe le système initialise automatiquement les super-classes, il n'est donc pas nécessaire de faire appelle à la méthode "initialize" de super. En revanche si une classe n'implémente pas la méthode "initialize" le système rappelle la méthode de sa super-classe (ou bien des niveaux supérieurs...) ce qui fait un assez gros merdier dans notre cas.

En effet, si seul Personnel implémente la méthode et que ses sous-classes ne l'implémentent pas alors à chaque fois qu'on créera le premier objet d'une des sous-classes, la variable static sera remise à zéro à chaque fois, et ça ce n'est pas bon, on peut se retrouver avec plusieurs personnes avec le même numéro... Alors la solution est d'utiliser l'une des méthodes de classe de NSObject : "class", le receveur de ce message renvoie le nom de sa classe, l'objet retourné est l'objet-classe lui-même. Comme initializer est une méthode de classe, le receveur ne peut-être qu'un objet-classe le mot clé "self" représente donc cette objet-classe, il est alors possible de comparer "self" avec la valeur retournée par la méthode "class".
Cela donne le code suivant :
if ( self == [Personnel class] ) { }

Ainsi, tout le code contenu entre les accolades ne sera exécuté que si le receveur est Personnel donc une seule et unique fois. Voilà pour le reste il n'y a pas de grande difficulté. Mise à part la gestion des copies dans notre méthode "setNumTel:", je vous parlerai plus en détail de cela plus tard. En effet, en Objective-C, il est préférable lorsqu'on affecte un objet à une variable d'instance de ne pas faire de copie, c'est-à-dire réserver une nouvelle zone mémoire et y placer les mêmes valeurs. Au lieu de ça, on relie la variable d'instance (la variable de l'objet) à l'adresse de l'objet que l'on veut lui affecter, cela permet des économies.

Maintenant, regardons notre petite calculPaie: en fait elle n'est là que pour cause de polymorphisme. Le polymorphisme est l'un des concepts de la programmation objet, il s'agit de la capacité d'une méthode à donner des réponses différentes selon le type de l'objet sur lequel la méthode est appelée. Imaginons la méthode "lancer", je suis pas sûr qu'une balle de golf et un chat réponde de la même manière à une telle méthode . C'est de ça qu'il s'agit. Deux classes peuvent implémenter une méthode portant le même nom, mais donner un résultat différent. Ici, cette méthode retourne pratiquement rien (zéro c'est pas grand chose), mais c'est juste parce que le compilateur, contrairement au C++ et au Java, vous renvoie un avertissement (permet l'exécution mais peut provoquer des bogues) si une méthode est déclarée dans l'en-tête mais qu'elle n'est pas implémentée ou qu'elle ne retourne pas la valeur souhaitée. Vous verrez plus tard comment écrire de façon plus élégante et compréhensible en Objective-C .

 

Les Sous-Classes Employe et Directeur

 

À présent passons aux classes Employe et Directeur. Dans la première partie, vous pouvez voir leur position dans l'arbre, elles se trouvent au même niveau et possède chacune deux fichiers. Commençons par la désormais coutumière @interface.

Ces petites classes héritent de Personnel, c'est donc tout naturellement que les deux points suivant leur noms sont eux-même suivi de "Personnel" car tout employé et tout Directeur est un Personnel. Après cela peu de difficulté, vous avez deux nouvelles variables de classe pour le calcul du salaire, elles possèdent ensuite leurs accesseurs correspondant, notre méthode calculPaie: qui servira à donner le salaire de l'employé ou du directeur et la méthode de désallocation de la mémoire. Petite nouveauté pour les initialiseurs, déjà le nom de la méthode de classe de création d'un employé et d'un directeur a changé, mais aussi les 4 initialiseurs ont 2 paramètres en plus... En fait, dans le cas de nos employés et de nos directeurs, lors de la création de l'objet, il faut entrer les informations requises pour les employés et les directeurs ainsi que celles requises pour les personnels, vu que l'initialiseur désigné de la classe Personnel les demande, c'est pour ça que nous avons autant de paramètres, d'où l'intérêt de les nommer explicitement. Je limite le nombre d'initialiseur, on a déjà vu comment on faisait. Voilà pour les fichiers d'en-tête. Si vous avez des problèmes à ce niveau... Inquiétez-vous car il n'y a rien de vraiment nouveau . Vous remarquerez aussi que je n'ai pas mis de méthode initializer en effet elle est inutile ici, il n'y a rien à initialiser au niveau de la classe.

On passe maintenant aux fichiers sources, c'est-à-dire la partie @implementation, ici non plus, pas beaucoup de difficultés, la programmation malgré les nouveaux concepts ça reste beaucoup de répétition. Vous remarquez cependant dans l'initialiseur d'instance, que nous utilisons la méthode désignée de la super-classe Personnel. Nous pouvons voir que la méthode description utilise elle aussi la méthode description de sa super-classe, cela permet d'économiser du texte à écrire . Et pour en finir avec les codes sources de ces classes, vous voyez que calculPaie renvoie une valeur plus logique.

 

La Sous-Classe Commercial

 

Voilà, nous pouvons passer au dernier niveau de notre arbre, la classe Commercial. Cette classe est très simple aussi, et c'est encore des répétitions. Vous remarquez que les initialiseurs d'instance ont maintenant 6 paramètres et ça commence à faire vraiment beaucoup, mais vous pouvez créer vos objets de façons différentes, par exemple mettre le salaire grâce à une autre méthode et mettre une valeur par défaut avec l'initialiseur. Vous remarquez à nouveau que les méthodes calculPaie et description utilise les résultats des méthodes de super c'est-à-dire ici d'Employe.

Encore une fois je passe vite sur tous ces détails car il y a pas de nouveauté par rapport à précédemment.

 

Utilisation de nos classes

 

Vous pouvez voir dans le fichier main.m une manière d'utiliser les classes que nous avons créé, il vous suffira de copier coller le code situé dans le main, dans le main de votre code pour voir le résultat. Vous remarquerez alors que les descriptions de chaque personnel change selon le type de même pour la calcul de la paye, c'est signe que le polymorphisme fonctionne correctement !

 

Les @protocol ou le pseudo-multi-héritage

 

Dans la partie précédentz, je vous avais dit que, contrairement au C++, le java et l'objective-c n'utilise que l'héritage simple, c'est-à-dire qu'une classe ne peut hérité que d'une seule super-classe. Cependant, plusieurs classes ne se trouvant pas dans les mêmes branches de l'arborescences peuvent avoir besoin de réagir aux mêmes méthodes. Dans notre cas, nous pouvons voir la méthode "calculPaie", auquel chacune de nos classes doivent pouvoir répondre, sauf la classe Personnel, pour laquelle ce n'est pas logique de calculer la paye puisqu'il n'y aucune valeur à retourner. Nous utilisons donc les protocoles en Objective-C pour permettre ce tour de passe-passe.

 

Déclaration

Pour déclarer un protocole, c'est-à-dire finalement le créer, rien de plus simple, on le déclare comme l'interface d'une classe sans les accolades et on remplace @interface par @protocol . Le principe du protocole étant de déclarer des méthodes qui seront implémentées par les classes qui se conformerons au protocole, vous n'avez aucun code à taper ! De même, que le protocole déclare les méthodes vous n'avez plus besoin de les déclarer dans l'interface de vos classes, vous pouvez donc retirer "- (float) calculPaie;" de vos 3 fichiers : Employe.h, Commercial.h et Directeur.h.

Étant donné qu'il doit être déclaré dans un en-tête et puis être importé par les classes qui l'utilise... Autant déclarer le protocole à la suite de la déclaration de la classe Personnel, comme ça on est sûr que les autres classes l'auront. Voilà notre petit protocole :

@protocol Paiement - (float) calculPaie; @end

Vous remarquez déjà qu'il n'y a pas de classe ou de protocole parent, en effet il n'y a pas de hiérarchie parmi les protocoles, vous pouvez en faire une si vous avez besoin mais aucune obligation (en lui faisant implémenté des protocoles). Le nom d'un protocole répond aux mêmes conventions que les noms de classes, essayez de donner un nom explicite.

 

Implémentation

Pour qu'une classe implémente un protocole, on dit qu'une classe se conforme à un protocole, il suffit d'ajouter sur la ligne @interface, avant l'accolade, ceci : , exemple :

@interface Employe : Personnel { // Variables d'instances } // Methodes - (float) calculPaie; @end

Une classe peut implémenter plusieurs protocoles, il suffit de les séparer par des virgules entre les chevrons ( ). Bien sûr, une fois le protocole implémenté, la classe doit implémenter les méthodes que le protocole déclare. Il faut aussi remarquer que lorsqu'une classe implémente un protocole, toutes les classes héritant de celle-ci implémente elles aussi ce protocole qu'on l'indique ou pas. Indiquer qu'une sous-classe hérite d'un protocole dont la super-classe hérite n'a d'intérêt que pour mieux vous repérer.

Finalement, vous devez faire ces changements :

  1. Écrire le protocole Paiement à la suite de l'@interface de la classe Personnel
  2. Supprimer la déclaration et l'implémentation de la méthode "calculPaie" de la classe Personnel
  3. Supprimer la déclaration et non l'implémenation de la méthode "calculPaie" des classes Employe, Directeur et Commercial
  4. Ajouter "" sur la ligne @interface de Employe, Directeur et Commercial

Vous pouvez ensuite exécuter votre programme une fois de plus et vous ne remarquerez aucun changement, ce qui est normal, nous avons fait que des modifications syntaxiques et non sémantiques. Vous pouvez cependant voir l'intérêt d'un protocole pour faire partager des actions portant le même nom à des classes différentes et sans rapport entre elles.

 

Les catégories : Extensions de classe

Autant le concept des protocoles existe dans Java (sous forme d'interface) autant celui des catégories ne se trouve que dans l'Objective-C. Ce système permet d'étendre les classes déjà créées sans avoir même besoin de connaître le code de la classe originelle. Attention vous ne pouvez qu'ajoutez des méthodes et pas des variables d'instances, il est aussi possible de masquer les méthodes de la classe par des méthodes de même nom dans la catégorie. Une fois la catégorie créée, elle fait partie intégrante de la classe, c'est-à-dire que le compilateur s'attend à ce que tous les objets de cette classe répondent aux méthodes de la classe ainsi que celles de ses catégories. Les méthodes d'une catégorie ont aussi accès à toutes les variables d'instance de la classe qu'elles étendent, y compris les variables déclarées comme @private. Il n'est d'ailleurs pas nécessaire de connaître le code source de la classe à étendre pour faire une catégorie, vous pouvez aussi bien étendre une classe conçue par NeXTSTEP (comme NSArray). Mais ce système est aussi très utilisé par Apple pour classer les méthodes, en effet, si vous regarder les fichiers d'en-tête contenu dans la framework Foundation, vous pourrez voir des tas de catégories. Il n'est pas rare de voir définies dans l'interface de la classe quelques méthodes de bases puis dans d'autres catégories des méthodes regroupées par thèmes. Cela rendra votre code plus clair.

Mais sinon, comment définit-on une catégorie ? Une catégorie ressemble beaucoup à une classe, seulement il n'y a pas à mettre la super-classe et pas d'accolade. Sinon vous définissez l'interface dans un fichier d'en-tête et l'implémentation dans un fichier source tout comme les classes.

Voici la syntaxe dans le fichier d'en-tête (.h) :

@interface nomDeLaClasseEtendue (nomDeLaCategorie)
// Déclaration des méthodes
@end

Voici la syntaxe dans le fichier source (.m) :

@implementation nomDeLaClasseEtendue (nomDeLaCategorie)
// Implémentation des méthodes
@end

Rien de plus simple ! Donc dans notre exemple, puisque je vais vous en faire un petit quand même, on va mettre la catégorie dans les fichiers de la classe qu'elle étend pas besoin d'en faire plus. Nous allons créer une catégorie pour la classe Directeur et ajouté deux petites méthodes histoire de faire la démonstration.

Voici ce que vous pouvez ajouter à la suite de l'interface de la classe Directeur :

@interface Directeur (Direction)
- (NSString*) description;
- (NSString*) licenciement;
@end

Ici, nous masquons la méthode description par une nouvelle version. Notez que si vous faites plusieurs catégories avec plusieurs versions d'une même méthode le comportement sera alors indéfini. Et voilà ce qu'on va rajouter à la suite de son implémentation :

@implementation Directeur (Direction)
- (NSString*) description
{
return [NSString stringWithFormat:@"Nom du directeur : %@\n
Telephone : %@\n
Indemnites : %.2f\n
Prime : %.2f\n
Remuneration : %.2f\n",

nomPers, (numTel ? numTel : @"Aucun"),
indemnites,prime,[self calculPaie]];
}
- (NSString*) licenciement
{
float indemn = 4 * indemnites;
return [NSString stringWithFormat:@"Indemnites de
licenciement : %.2f"
,indemn];
} @end

Vous pouvez tenter la compilation vous verrez les modifications que l'on a apporté grâce à la catégorie ! Vous pouvez de même tester la nouvelle méthode grâce au message : "[d3 licenciement]".

Voici donc ce qui clôt ce deuxième chapitre sur l'Objective-C qui, je suis désolé, a mis beaucoup de temps à venir. Il est assez lourd, il y a beaucoup de choses, mais peu de difficultés, je vais tout de même vous faire un résumé dans le chapitre suivant qui prendra, je vous le promets, moins de temps à venir.

Si vous avez des questions, des suggestions, des commentaires, vous pouvez me contacter par les voies habituelles : section Contacter, Forum, ...

Psycho

Lire une autre news :
Actualités
Tweeter

La Programmation : Le Langage Objective-C (2)

Actualité rédigée par le 16/01/2007 à 23:56
La Programmation : Le Langage Objective-C (2)

La Programmation : Le Langage Objective-C (2)

 

Sujets abordés :

  • Utilisation d'une classe
  • Héritage
  • Les Protocoles
  • Les Catégories

Bonjour à tous et bienvenue dans ce nouveau tutoriel sur la programmation en Objective-C avec l'API Cocoa d'Apple. Tout d'abord merci à tous pour vos encouragements, ça fait plaisir et ça donne envie de continuer . Je vous invite à revoir l'épisode précédent afin d'y prendre en compte les modifications que j'y ai apporté, pour y accéder, vous passez par la section "tutoriaux" dans la partie "La Programmation".

Je suis désolé pour l'attente de quelques mois que je vous ai faite subir, mais je vais chercher à être plus rapide maintenant dans l'écriture de ce tutoriel. Aujourd'hui, nous allons nous attaquer à l'utilisation d'une classe, ainsi que la gestion de la mémoire mais aussi et surtout au concept d'héritage, concept majeur de la POO et donc de l'Objective-C !

 

 

La Classe Personnel

 

Utilisation de notre classe

Alors, pour l'instant notre classe n'est pas très volumineuse, il n'y a pas grand chose dedans ! Mais bon on peut toujours en faire quelque chose. Je précise que je ne ferais pas d'interaction avec l'utilisateur ici (en utilisant printf/scanf), tout sera programmé en dur dans le code, les interactions n'apportent en effet pas grand chose en l'occurrence, du moment que vous connaissez le code et que vous voyez le résultat.

Notre programme se basera donc essentiellement sur la fonction C NSLog() . Et nous allons commencer par créer 3 nouveaux personnels pour vous montrer comment on utilise notre petite classe ! En implémentant vos méthodes, vous avez déjà eu l'occasion de voir ce système, seulement là, nous allons utiliser nos méthodes à nous, vous verrez que rien ne change. Pour commencer, il faut retourner dans votre fichier principal, il porte le nom de votre projet et contient la fonction main(). Chez moi, ce fichier se nomme "Tutoriel_CLU.m". Déjà, vous pouvez effacer tout ce qui se trouve entre "NSAutoreleasePool..." et "[pool release] " mais n'effacez pas ces balises, nous y reviendrons plus tard. Entre elles vous pouvez écrire tout ça :

Personnel *p1 = [[Personnel alloc] initWithNom:@"Psycho"
andNumTel:@"000444"];

Personnel *p2 = [Personnel personnelWithNom:@"Tom"
andNumTel:@"3554"];

id p3 = [Personnel personnelWithNom:@"Zeus"];

NSLog(@"\nListe :\n%@\n%@\n%@\n",p1,p2,p3);

[p3 setNumTel:[p1 numTel]];

NSLog(@"\nNom de p2 : %@\np3 :\n%@\n",[p2 nomPers],p3);

[p1 release];
[p2 release];
[p3 release];

Voilà, voilà... Nous commençons notre petit programme par une série de déclaration / allocation / initialisation d'objets. Dans le premier cas nous déclarons un pointeur sur un Personnel, il ne faut pas oublier qu'il faut toujours déclarer des pointeurs sur objet ! Donc nous déclarons p1 de type "Personnel *" et puis nous l'allouons et l'initialisons à l'aide de notre méthode désignée qu'on a vu dans le précédent chapitre. Nous faisons de même pour p2 en utilisant cette fois-ci notre méthode de classe qui fait, je vous le rappelle, l'allocation et l'initialisation elle-même, dans tous les cas cette instruction a le même effet que la précédente (aux arguments près) et nous avons ainsi deux nouveaux "Personnel" ayant chacun des valeurs différentes.

Dans le cas de la troisième instruction, vous remarquerez que j'ai remplacé "Personnel *" par le mot clé "id". Ce mot clé représente en fait un pointeur sur un objet, d'où l'absence de l'étoile, sur un NSObject, c'est-à-dire un objet sans aucune autre spécificité que d'être un objet. Ce mot clé permet de faire du "typage dynamique", il s'agit d'un concept important de l'Objective-C et qui permet de moins s'embêter avec toutes les classes, en fait, on ne précise pas au compilateur quelle classe d'objet sera affectée à cette valeur et ce ne sera que lorsque l'application tournera que le type de l'objet sera connu. Si vous voulez, vous pouvez déclarer des "id" partout sans distinction, à chaque fois que vous avez besoin d'un objet, vous mettez un "id", le problème c'est que vous ne donnez aucune information au compilateur et que si vous envoyez à cet objet un message auquel il n'est pas capable de répondre, vous n'obtiendrez pas le résultat souhaité ce qui peut être dommageable. Si vous êtes sûr du type d'objet que vous souhaitez affecté à votre variable, je ne vous conseillerais que trop de le préciser.

Toujours est-il qu'on affecte à notre variable de type "id" un "Personnel" grâce à notre méthode de classe
"personnelWithName: ", donc lorsque l'application tournera, cette objet sera un Personnel.

Dans l'instruction qui suit, on utilise la fonction C "NSLog() " qui nous permet d'afficher un message sur la console, en fait cette fonction rédige un "rapport" d'erreur (fichier log) et nous ne faisons que l'afficher dans la console afin de voir l'état de nos objets. Quand vous exécutez le programme vous obtenez la liste des personnels avec leur nom et numéro de téléphone, dans le cas de Zeus ce dernier est absent.

Justement, dans l'instruction suivante nous utilisons la méthode "setNumTel:", qui nous permet d'affecter un numéro de téléphone à notre objet, et nous donnons comme paramètre une autre méthode ! C'est tout à fait normal, puisque cette méthode "numTel", renvoie une "NSString" et c'est ce dont nous avons besoin dans notre méthode "setNumTel:", ainsi "p3" aura le même numéro de téléphone que "p1". Et pour finir, nous faisons un nouvel affichage pour contrôler que le changement a bien eu lieu.

 

Héritage

Voilà on peut entamer l'héritage, concept très important de l'Objective-C et de la programmation objet en général. L'héritage est le fait de mettre en relation 2 classes, mais d'une façon spécifique, la relation entre super-classe et sous-classe est de type "est un". Prenons un exemple dans la nature. La biologie a cette avantage de présenter un nombre incroyable de classification et sous-classification. On va donc reprendre l'exemple de notre Chat, comme tout le monde le sait, enfin j'espère, le Chat "est un" Félin, tout comme le Tigre ou le Lion, on déjà un début d'arborescence : la racine c'est "Félin" est on a nos branches : "Chat", "Lion", "Tigre". Chacun de ces animaux sont des Félins, c'est-à-dire qu'ils ont des caractères communs tels que la forme du crâne, les pupilles en amande, les griffes rétractiles, etc. Mais bien qu'ils soient de la même famille, on ne peut pas dire que ces Félins sont pareils, ils ont des caractères propres, par exemple le Tigre a le pelage rayé, le Lion a une crinière et le chat miaule et ronronne. Donc l'héritage c'est ça, c'est "factoriser", autrement dit réunir, du code commun à plusieurs classes afin d'en économiser la réécriture, qui peut être fastidieuse à force ! Si on en revient à nos félins, on peut dire sans peine que ce sont des mammifères tout comme les canidés ou les primates, et parmi ces primates on compte les chimpanzés, les singes et les humains. Tout comme le chat, les primates on des mamelles et des poils puisqu'ils sont mammifères, en revanche ce qui les différencient des chats c'est le fait qu'ils ont des ongles et non pas des griffes, ou bien des yeux avec pupille ronde et non en amande. Et puis la différence entre singe et humain c'est qu'on marche généralement sur deux pattes plutôt que 4, etc.

Donc en programmation c'est exactement le même principe, la super-classe permet de partager du code entre ses sous-classes pour ne pas avoir à le réécrire, chacune de ces sous-classes peut être instanciée comme étant un objet de sa super-classe, etc. Mais ne vous inquiétez pas nous allons y revenir. Déjà, ce qu'il faut savoir c'est qu'en Objective-C, comme en Java, l'héritage est simple, c'est-à-dire qu'une classe ne peut avoir qu'une et une seule super-classe, en revanche cette super-classe peut avoir une infinité de sous-classe, on peut remarquer aussi que toutes les classes héritent directement ou indirectement de la classe racine (root class) NSObject, en Java il s'agit de la classe Object, c'est-à-dire que toute classe est un objet et hérite de méthode tel que l'allocation, désallocation, description, release, etc. C'est une grosse différence avec le langage C++ qui lui possède un héritage multiple et pas de super-classe racine cela pose des problèmes lorsqu'il faut par exemple créer des méthodes d'affichage, on est obligés d'utiliser un concept obscure nommé l'amitié... En revanche il peut être utile de donner à des classes différentes dans des branches différentes (c'est-à-dire qui héritent de super-classes différentes) sans aucun caractère en commun, et bien justement, des caractères commun, le Java et l'Objective-C permettent de parer à ce problème grâce au système des interfaces (pour Java) et des protocoles (pour Objective-C). Si une classe ne peut hériter que d'une super-classe, en revanche elle peut hériter d'une infinité de protocole. Mais on parlera de ça plus loin de ça, ça peut-être utile dans de grosses arborescences.

 

Alors je vous le dis tout de suite, je suis assez flemmard mais aussi l'écriture de ce qui suit dans le programme est plutôt fastidieuse, alors au lieu de vous écrire tout ce qu'il faut mettre je vais vous donner les fichiers à télécharger pour les ajouter le plus simplement du monde à votre projet. Nous allons y ajouter 3 nouvelles classes pour vous exposer concrètement la notion d'héritage. Voici donc, un petit diagramme de classes généré à l'aide d'Xcode :

Ceci est un diagramme UML plus ou moins formalisé. Dans le cartouche du rectangle vous avez le nom de la classe, puis en-dessous vous avez deux espaces, nommés ici : Properties (propriétés) et indiquant tous les attributs de votre classe suivi de leur type, puis le champ Operations (opérations) contenant toutes les méthodes de votre classe, les méthodes non-soulignées sont les méthodes d'instance et les méthodes soulignées sont les méthodes de classe. Ce que ce diagramme nous montre de très intéressant, ce sont les flèches, les flèches qui indiquent quelle classe hérite de quelle classe, en l'occurrence 3 des classes héritent de Personnel, directement ou indirectement, et Personnel hérite de NSObject qui n'est pas indiquée ici.

Maintenant, je vous propose de télécharger ce petit fichier au format ".SITX" : tutoriel.sitx pour télécharger appuyez sur la touche "alt" en cliquant sur le lien.

Le dossier contient 10 fichiers :

  1. En-Tete.h : Il permettra de simplifier l'importation de tous les fichiers d'en-tête de classes.
  2. Personnel.h : Fichier d'en-tête de la classe Personnel
  3. Personnel.m : Fichier source de la classe Personnel
  4. Employe.h : Fichier d'en-tête de la classe Employe
  5. Employe.m : Fichier source de la classe Employe
  6. Directeur.h : Fichier d'en-tête de la classe Directeur
  7. Directeur.m : Fichier source de la classe Directeur
  8. Commercial.h : Fichier d'en-tête de la classe Commercial
  9. Commercial.m : Fichier source de la classe Commercial
  10. main.m : Contient un petit programme utilisant les classes.

Contrairement à ce qui serait souhaitable, aucun de ces fichiers n'est commenté ! Mais bon en l'occurrence ce n'est pas grave puisque je vais tout vous expliquer en détail dans ce tutoriel.

Pour commencer, le premier fichier n'est pas particulièrement compliqué, d'ailleurs il ne s'agit pas d'une classe mais juste d'un fichier permettant de réunir les "#import" à faire dans le fichier source principal. Donc au début du fichier ".m" contenant la fonction "main()" je remplace tous les "#import" par un simple : "#import "En-Tete.h"" ainsi je ne surcharge pas le code.

Alors voilà, maintenant on passe aux deux fichiers suivants concernant notre classe "Personnel". Cette petite classe a subit pas mal de modifications depuis la diffusion du premier épisode à cause de (ou grâce à???) mes diverses recherches sur l'Objective-C qui m'ont permises d'améliorer son code.

Vous pouvez remarquer l'apparition de la méthode de classe "+ (void) initialize; ", cette petite méthode existant déjà dans la classe NSObject permet d'initialiser la classe, c'est-à-dire d'initialiser des valeurs, de mettre en place différentes variable... enfin tout ce qui doit être fait à la création de votre classe. Cette méthode n'est pas forcément appelée, en fait elle est appelée juste avant l'envoie du premier message à votre classe. Vous avez aussi une nouvelle méthode : "calculPaie:" dont on va reparler plus tard, et qui va encore un peu bouger à l'avenir. Voilà pour le fichier d'en-tête.

En ce qui concerne le fichier source, la première modification est la variable " static", il s'agit de notre variable de classe... écrite ainsi dans l'implémentation de la classe (entre les bornes @implementation et @end elle devient alors une variable privée (ce n'est d'ailleurs pas négociable ) les objets partageront la même variable avec la même valeur. Ici, nous voyons qu'elle n'est pas initialisée c'est quelque chose que nous allons faire dans notre fameuse méthode
"initialize". Mais revenons-y ! Je vous ai dit que cette méthode est appelée juste avant le premier message envoyé à un objet de votre classe (ou à la classe elle-même), mais les super-classes de votre méthode doivent être elles aussi initialisées. Il n'y a pas à s'inquiéter, avant d'initialiser votre classe le système initialise automatiquement les super-classes, il n'est donc pas nécessaire de faire appelle à la méthode "initialize" de super. En revanche si une classe n'implémente pas la méthode "initialize" le système rappelle la méthode de sa super-classe (ou bien des niveaux supérieurs...) ce qui fait un assez gros merdier dans notre cas.

En effet, si seul Personnel implémente la méthode et que ses sous-classes ne l'implémentent pas alors à chaque fois qu'on créera le premier objet d'une des sous-classes, la variable static sera remise à zéro à chaque fois, et ça ce n'est pas bon, on peut se retrouver avec plusieurs personnes avec le même numéro... Alors la solution est d'utiliser l'une des méthodes de classe de NSObject : "class", le receveur de ce message renvoie le nom de sa classe, l'objet retourné est l'objet-classe lui-même. Comme initializer est une méthode de classe, le receveur ne peut-être qu'un objet-classe le mot clé "self" représente donc cette objet-classe, il est alors possible de comparer "self" avec la valeur retournée par la méthode "class".
Cela donne le code suivant :
if ( self == [Personnel class] ) { }

Ainsi, tout le code contenu entre les accolades ne sera exécuté que si le receveur est Personnel donc une seule et unique fois. Voilà pour le reste il n'y a pas de grande difficulté. Mise à part la gestion des copies dans notre méthode "setNumTel:", je vous parlerai plus en détail de cela plus tard. En effet, en Objective-C, il est préférable lorsqu'on affecte un objet à une variable d'instance de ne pas faire de copie, c'est-à-dire réserver une nouvelle zone mémoire et y placer les mêmes valeurs. Au lieu de ça, on relie la variable d'instance (la variable de l'objet) à l'adresse de l'objet que l'on veut lui affecter, cela permet des économies.

Maintenant, regardons notre petite calculPaie: en fait elle n'est là que pour cause de polymorphisme. Le polymorphisme est l'un des concepts de la programmation objet, il s'agit de la capacité d'une méthode à donner des réponses différentes selon le type de l'objet sur lequel la méthode est appelée. Imaginons la méthode "lancer", je suis pas sûr qu'une balle de golf et un chat réponde de la même manière à une telle méthode . C'est de ça qu'il s'agit. Deux classes peuvent implémenter une méthode portant le même nom, mais donner un résultat différent. Ici, cette méthode retourne pratiquement rien (zéro c'est pas grand chose), mais c'est juste parce que le compilateur, contrairement au C++ et au Java, vous renvoie un avertissement (permet l'exécution mais peut provoquer des bogues) si une méthode est déclarée dans l'en-tête mais qu'elle n'est pas implémentée ou qu'elle ne retourne pas la valeur souhaitée. Vous verrez plus tard comment écrire de façon plus élégante et compréhensible en Objective-C .

 

Les Sous-Classes Employe et Directeur

 

À présent passons aux classes Employe et Directeur. Dans la première partie, vous pouvez voir leur position dans l'arbre, elles se trouvent au même niveau et possède chacune deux fichiers. Commençons par la désormais coutumière @interface.

Ces petites classes héritent de Personnel, c'est donc tout naturellement que les deux points suivant leur noms sont eux-même suivi de "Personnel" car tout employé et tout Directeur est un Personnel. Après cela peu de difficulté, vous avez deux nouvelles variables de classe pour le calcul du salaire, elles possèdent ensuite leurs accesseurs correspondant, notre méthode calculPaie: qui servira à donner le salaire de l'employé ou du directeur et la méthode de désallocation de la mémoire. Petite nouveauté pour les initialiseurs, déjà le nom de la méthode de classe de création d'un employé et d'un directeur a changé, mais aussi les 4 initialiseurs ont 2 paramètres en plus... En fait, dans le cas de nos employés et de nos directeurs, lors de la création de l'objet, il faut entrer les informations requises pour les employés et les directeurs ainsi que celles requises pour les personnels, vu que l'initialiseur désigné de la classe Personnel les demande, c'est pour ça que nous avons autant de paramètres, d'où l'intérêt de les nommer explicitement. Je limite le nombre d'initialiseur, on a déjà vu comment on faisait. Voilà pour les fichiers d'en-tête. Si vous avez des problèmes à ce niveau... Inquiétez-vous car il n'y a rien de vraiment nouveau . Vous remarquerez aussi que je n'ai pas mis de méthode initializer en effet elle est inutile ici, il n'y a rien à initialiser au niveau de la classe.

On passe maintenant aux fichiers sources, c'est-à-dire la partie @implementation, ici non plus, pas beaucoup de difficultés, la programmation malgré les nouveaux concepts ça reste beaucoup de répétition. Vous remarquez cependant dans l'initialiseur d'instance, que nous utilisons la méthode désignée de la super-classe Personnel. Nous pouvons voir que la méthode description utilise elle aussi la méthode description de sa super-classe, cela permet d'économiser du texte à écrire . Et pour en finir avec les codes sources de ces classes, vous voyez que calculPaie renvoie une valeur plus logique.

 

La Sous-Classe Commercial

 

Voilà, nous pouvons passer au dernier niveau de notre arbre, la classe Commercial. Cette classe est très simple aussi, et c'est encore des répétitions. Vous remarquez que les initialiseurs d'instance ont maintenant 6 paramètres et ça commence à faire vraiment beaucoup, mais vous pouvez créer vos objets de façons différentes, par exemple mettre le salaire grâce à une autre méthode et mettre une valeur par défaut avec l'initialiseur. Vous remarquez à nouveau que les méthodes calculPaie et description utilise les résultats des méthodes de super c'est-à-dire ici d'Employe.

Encore une fois je passe vite sur tous ces détails car il y a pas de nouveauté par rapport à précédemment.

 

Utilisation de nos classes

 

Vous pouvez voir dans le fichier main.m une manière d'utiliser les classes que nous avons créé, il vous suffira de copier coller le code situé dans le main, dans le main de votre code pour voir le résultat. Vous remarquerez alors que les descriptions de chaque personnel change selon le type de même pour la calcul de la paye, c'est signe que le polymorphisme fonctionne correctement !

 

Les @protocol ou le pseudo-multi-héritage

 

Dans la partie précédentz, je vous avais dit que, contrairement au C++, le java et l'objective-c n'utilise que l'héritage simple, c'est-à-dire qu'une classe ne peut hérité que d'une seule super-classe. Cependant, plusieurs classes ne se trouvant pas dans les mêmes branches de l'arborescences peuvent avoir besoin de réagir aux mêmes méthodes. Dans notre cas, nous pouvons voir la méthode "calculPaie", auquel chacune de nos classes doivent pouvoir répondre, sauf la classe Personnel, pour laquelle ce n'est pas logique de calculer la paye puisqu'il n'y aucune valeur à retourner. Nous utilisons donc les protocoles en Objective-C pour permettre ce tour de passe-passe.

 

Déclaration

Pour déclarer un protocole, c'est-à-dire finalement le créer, rien de plus simple, on le déclare comme l'interface d'une classe sans les accolades et on remplace @interface par @protocol . Le principe du protocole étant de déclarer des méthodes qui seront implémentées par les classes qui se conformerons au protocole, vous n'avez aucun code à taper ! De même, que le protocole déclare les méthodes vous n'avez plus besoin de les déclarer dans l'interface de vos classes, vous pouvez donc retirer "- (float) calculPaie;" de vos 3 fichiers : Employe.h, Commercial.h et Directeur.h.

Étant donné qu'il doit être déclaré dans un en-tête et puis être importé par les classes qui l'utilise... Autant déclarer le protocole à la suite de la déclaration de la classe Personnel, comme ça on est sûr que les autres classes l'auront. Voilà notre petit protocole :

@protocol Paiement - (float) calculPaie; @end

Vous remarquez déjà qu'il n'y a pas de classe ou de protocole parent, en effet il n'y a pas de hiérarchie parmi les protocoles, vous pouvez en faire une si vous avez besoin mais aucune obligation (en lui faisant implémenté des protocoles). Le nom d'un protocole répond aux mêmes conventions que les noms de classes, essayez de donner un nom explicite.

 

Implémentation

Pour qu'une classe implémente un protocole, on dit qu'une classe se conforme à un protocole, il suffit d'ajouter sur la ligne @interface, avant l'accolade, ceci : , exemple :

@interface Employe : Personnel { // Variables d'instances } // Methodes - (float) calculPaie; @end

Une classe peut implémenter plusieurs protocoles, il suffit de les séparer par des virgules entre les chevrons ( ). Bien sûr, une fois le protocole implémenté, la classe doit implémenter les méthodes que le protocole déclare. Il faut aussi remarquer que lorsqu'une classe implémente un protocole, toutes les classes héritant de celle-ci implémente elles aussi ce protocole qu'on l'indique ou pas. Indiquer qu'une sous-classe hérite d'un protocole dont la super-classe hérite n'a d'intérêt que pour mieux vous repérer.

Finalement, vous devez faire ces changements :

  1. Écrire le protocole Paiement à la suite de l'@interface de la classe Personnel
  2. Supprimer la déclaration et l'implémentation de la méthode "calculPaie" de la classe Personnel
  3. Supprimer la déclaration et non l'implémenation de la méthode "calculPaie" des classes Employe, Directeur et Commercial
  4. Ajouter "" sur la ligne @interface de Employe, Directeur et Commercial

Vous pouvez ensuite exécuter votre programme une fois de plus et vous ne remarquerez aucun changement, ce qui est normal, nous avons fait que des modifications syntaxiques et non sémantiques. Vous pouvez cependant voir l'intérêt d'un protocole pour faire partager des actions portant le même nom à des classes différentes et sans rapport entre elles.

 

Les catégories : Extensions de classe

Autant le concept des protocoles existe dans Java (sous forme d'interface) autant celui des catégories ne se trouve que dans l'Objective-C. Ce système permet d'étendre les classes déjà créées sans avoir même besoin de connaître le code de la classe originelle. Attention vous ne pouvez qu'ajoutez des méthodes et pas des variables d'instances, il est aussi possible de masquer les méthodes de la classe par des méthodes de même nom dans la catégorie. Une fois la catégorie créée, elle fait partie intégrante de la classe, c'est-à-dire que le compilateur s'attend à ce que tous les objets de cette classe répondent aux méthodes de la classe ainsi que celles de ses catégories. Les méthodes d'une catégorie ont aussi accès à toutes les variables d'instance de la classe qu'elles étendent, y compris les variables déclarées comme @private. Il n'est d'ailleurs pas nécessaire de connaître le code source de la classe à étendre pour faire une catégorie, vous pouvez aussi bien étendre une classe conçue par NeXTSTEP (comme NSArray). Mais ce système est aussi très utilisé par Apple pour classer les méthodes, en effet, si vous regarder les fichiers d'en-tête contenu dans la framework Foundation, vous pourrez voir des tas de catégories. Il n'est pas rare de voir définies dans l'interface de la classe quelques méthodes de bases puis dans d'autres catégories des méthodes regroupées par thèmes. Cela rendra votre code plus clair.

Mais sinon, comment définit-on une catégorie ? Une catégorie ressemble beaucoup à une classe, seulement il n'y a pas à mettre la super-classe et pas d'accolade. Sinon vous définissez l'interface dans un fichier d'en-tête et l'implémentation dans un fichier source tout comme les classes.

Voici la syntaxe dans le fichier d'en-tête (.h) :

@interface nomDeLaClasseEtendue (nomDeLaCategorie)
// Déclaration des méthodes
@end

Voici la syntaxe dans le fichier source (.m) :

@implementation nomDeLaClasseEtendue (nomDeLaCategorie)
// Implémentation des méthodes
@end

Rien de plus simple ! Donc dans notre exemple, puisque je vais vous en faire un petit quand même, on va mettre la catégorie dans les fichiers de la classe qu'elle étend pas besoin d'en faire plus. Nous allons créer une catégorie pour la classe Directeur et ajouté deux petites méthodes histoire de faire la démonstration.

Voici ce que vous pouvez ajouter à la suite de l'interface de la classe Directeur :

@interface Directeur (Direction)
- (NSString*) description;
- (NSString*) licenciement;
@end

Ici, nous masquons la méthode description par une nouvelle version. Notez que si vous faites plusieurs catégories avec plusieurs versions d'une même méthode le comportement sera alors indéfini. Et voilà ce qu'on va rajouter à la suite de son implémentation :

@implementation Directeur (Direction)
- (NSString*) description
{
return [NSString stringWithFormat:@"Nom du directeur : %@\n
Telephone : %@\n
Indemnites : %.2f\n
Prime : %.2f\n
Remuneration : %.2f\n",

nomPers, (numTel ? numTel : @"Aucun"),
indemnites,prime,[self calculPaie]];
}
- (NSString*) licenciement
{
float indemn = 4 * indemnites;
return [NSString stringWithFormat:@"Indemnites de
licenciement : %.2f"
,indemn];
} @end

Vous pouvez tenter la compilation vous verrez les modifications que l'on a apporté grâce à la catégorie ! Vous pouvez de même tester la nouvelle méthode grâce au message : "[d3 licenciement]".

Voici donc ce qui clôt ce deuxième chapitre sur l'Objective-C qui, je suis désolé, a mis beaucoup de temps à venir. Il est assez lourd, il y a beaucoup de choses, mais peu de difficultés, je vais tout de même vous faire un résumé dans le chapitre suivant qui prendra, je vous le promets, moins de temps à venir.

Si vous avez des questions, des suggestions, des commentaires, vous pouvez me contacter par les voies habituelles : section Contacter, Forum, ...

Psycho

Lire une autre news :

Les commentaires sur Logiciel Mac

Commentaires Articles

Anonyme le 03/02/2007 02:51

Excellente 2eme partie de ce tuto. Merci pour tes efforts continus, PsychoH13. Petite remarque: il y a des petits erreur pour l'implementation de la categorie Paiement (eg le positionnement et la syntaxe du sur la ligne @interface n'est pas correcte). J'attends avec impatience la suite de ce tuto qui pourra bien devenir la reference pour l'objective C! :)

Crashray le 19/01/2007 07:50

Ouais enfin j'avais voulu faire une GUI pour mkvextract, c'est un truc en ligne de commande pour extraire les différentes pistes d'un mkv. Ma dernière version est dispo [URL=http://crashray.googlepages.com/imkvextract.rar] ici [/URL]. J'avais pas réussi à le faire tout seul. Ludo m'a beaucoup aidé O:-)

PsychoH13 le 19/01/2007 05:29

Bah je vais faire étape par étape :D$ Mais tu sais que généralement les comandes ont leur équivalent C ??? Et qui dit équivalent C dit intégration à l'Objective-C

Crashray le 18/01/2007 11:50

Super Psycho ! Si tu pouvais faire un article sur comment faire une Interface graphique et la lier à un prog en ligne de commande. Je pense que ça serait utile. Bonne continuation :p

Blackfight le 17/01/2007 10:50

BRAVO !!!!!!!!!! :D :D ;)

PsychoH13 le 17/01/2007 08:48

Ouais je sais FireFox a du mal avec le texte préformaté

Anonyme le 17/01/2007 08:47

trés intéressant mais il y'a un petit problème de mise en page de l'article sous firefox (page trés large).

Anonyme le 17/01/2007 08:36

tu as mis combien de temps pour tout écrire Psy? Aussi tu serais pas une machine? ;) :p

►► Voir plus de commentaires ◄◄
Laissez votre avis pour l'article La Programmation : Le Langage Objective-C (2) (Merci de rester correct et de respecter les différents intervenants.)
Veuillez entrer les caractères
de l’image ci-dessous :

Articles sur le même sujet

  • Les bons plans du mercredi
    Les bons plans du mercredi

    Little Snitch : arrêtez-vous sur cette promotion alléchante de Little Snitch, une application Mac développée...

    Lire la suite
  • Napoleon: Total War Gold Edition pour Mac annoncé au printemps
    Napoleon: Total War Gold Edition pour Mac annoncé au printemps

    Après le succès de Empire: Total War, Sega et Feral Interactive repartent en guerre avec le portage attendu du fameux...

    Lire la suite
  • Origin, une version Alpha arrive sur Mac
    Origin, une version Alpha arrive sur Mac

    Dans l’idée de convertir le plus grand nombre, les plateformes de téléchargement de jeux vidéo...

    Lire la suite
  • 10 nouveaux jeux Mac sur GOG
    10 nouveaux jeux Mac sur GOG

    Lancée en 2008 pour le plus grand bonheur des gamers nostalgiques, la plateforme éditée par CD Projekt et...

    Lire la suite
  • Apple : le titre dégringole malgré des chiffres record
    Apple : le titre dégringole malgré des chiffres record

    Apple vient de présenter les résultats financiers de son premier trimestre fiscal, qui s’étend de septembre...

    Lire la suite
  • Le Guide du Cloud est de retour
    Le Guide du Cloud est de retour

    Le guideducloud vient de subir un petit lifting : la nouvelle version de cet espace dédié à la suite Office en...

    Lire la suite
  • 10,000,000 enfin disponible pour Mac et PC sur Steam
    10,000,000 enfin disponible pour Mac et PC sur Steam

    Avec le succès grandissant des Puzzle-RPG sur iOS, l’éditeur de jeux EightyEight Games propose enfin sur Steam...

    Lire la suite

Les Apps les plus téléchargées

  • Télécharger AnyMP4 Audio Convertisseur pour Mac AnyMP4 Audio Convertisseur pour Mac -
  • Télécharger Apeaksoft Free Online Video Converter Apeaksoft Free Online Video Converter -
  • Télécharger OnyX OnyX - Optimisation
  • Télécharger VLC Media Player VLC Media Player - Lecteur Vidéo
  • Télécharger LiquidCD LiquidCD - Gravure
  • Télécharger Stuffit Expander Stuffit Expander - Compression
  • Télécharger Gimp Gimp - Imagerie

Suivez-nous

Suivez-nous Abonnez-vous
  • Contact
  • |
  • Annoncer sur LogicielMac.com
  • |
  • Mentions légales
  • |
  • CGU
  • |
  • Suivez-nous sur :
  • Twitter Twitter
  • Facebook Facebook
© 2025 Logiciel Mac
LogicielMac.com
  • Contact
  • Annoncer sur LogicielMac.com
  • Mentions légales
  • CGU
  • Suivez-nous sur :
  • Twitter Twitter
  • Facebook Facebook
LogicielMac.com
© 2025 Logiciel Mac