Architecture des applications
Résumé
Cette partie introduit les concepts qui décrivent les composants essentiels d’une application Cocoa et comment ils travaillent ensemble. Elle traite aussi des fonctionnalités des applications Cocoa et les principes relatifs à leur conception.
Vue d’ensemble
Trois importantes fonctionnalités des frameworks Cocoa — scripting, l’architecture document et annuler/rétablir- ont conceptuellement beaucoup de points communs. Cette partie explique leurs bases conceptuelles partagées. Elle ne rentre pas dans le détail des classes spécifiques mettant en oeuvre ces fonctionnalités ni n’explique comment les utiliser. Au lieu de cela, elle se concentre sur la structure recommendée d’une application et sur la façon dont cette structure supporte ces fonctionnalités.
Les frameworks Cocoa sont AppKit.framework et Foundation.framework. Vous pouvez les examiner dans /System/Library/Frameworks/.
Cette partie commence par la description du modèle de conception “Model-View-Controller” (MVC) (NDT : Ma précédente traduction) car ce modèle est celui qui supporte le mieux le scripting, les applications basées sur des documents et l’annulation (NDT : undo). Elle ne décrit pas en détail le modèle MVC, ce n’est pas vraiment le propos, mais elle le met suffisamment en avant pour planter le décor du reste du sujet.
Ce sujet utilise Objective-C pour décrire les API spécifiques. Toutefois, que ce soit le scripting, les documents ou l’annulation, les API existent aussi en Java. Certaines spécificités de Java sont abordées quand c’est nécessaire et si Java n’est pas mentionné spécifiquement, c’est qu’il n’y a rien de spécial à dire.
Quelques sujets qui auraient pu être sités dans cette partie sont placés à des endroits où les associations conceptuelles et architecturales sont plus profondes. Ces sujets en relation avec celui ci sont placés dans “Related Topics” (en anglais).
Le contenu d’une application Cocoa
Les applications Cocoa sont distribuées sous forme de paquets (NDT : pour éviter toute confusion, j’utiliserais le terme anglais “bundle”). Un bundle application est un type spécial de bundle : un répertoire qui se présente à l’utilisateur dans le Finder comme un seul fichier. Dans le cas d’un bundle application, le fichier est un exécutable. Le double-cliquer ordonne au Finder de lancer l’application. Les bundles application ont une extention .app.
Un bundle application contient l’exécutable et les ressources nécessaires par l’exécutable. Pour plus d’information au sujet des bundles et la construction de bundle application, allez voir les chapitres “Bundles” et “Applications Packaging” dans Inside Mac OS X : System Overview.
Sommaire de la partie architecture des applications
- Le modèle de conception “Model-View-Controller”
- L’architecture document
- Scripting
- Annuler et rétablir
- Fontionnalité d’une application Cocoa
- Le cycle des évènements et de l’affichage (non encore écrit par Apple, donc par moi non plus)
- les classes primaires d’application (idem ci-dessus)
Le modèle de conception “Model-View-Controller”
Note du traducteur : Pour faciliter la compréhension de ce concept, je n’ai volontairement pas traduit dans le texte les trois composantes de ce modèle de conception. Il s’agit de :
|
Model :
|
Modèle, maquette, description. |
|
View :
|
Vue, ce que l’on voit. |
|
Controller :
|
Contrôleur, dans le sens maîtrise, supervision. |
Introduction
Le modèle de représentation “Model-View-Controller” (MVC) est assez ancien. Il est apparu aux environs des premiers jours de Smalltalk. C’est un modèle de haut niveau dans le sens où il s’occupe de l’architecture globale d’un programme et essaie de fournir une classification des différents types d’objets qui composent une application.
D’après ce modèle, il y a trois types d’objets : les objets “model”, les objets “view” et les objets “controller”. le modèle définit les rôles que jouent ces types d’objets dans l’application; En tant que développeur, vous créez vos classes en fonction de ces trois groupes d’objets. Chacun de ces trois types d’objets est séparé des autres par des frontières abstraites, et communique avec les objets d’un autre type à travers ces frontières.
Les objets “model” représentent les données et les fonctionnalités de base
Les objets “model” représentent les connaissances et expertises spécifiques. Ils contiennent les données d’une application et définissent la logique de manipulation de ces données. Une application construite correctement sur le modèle MVC a ses données les plus importantes encapsulées dans les objets “model”. Toute donnée qui fait partie de l’état persistant d’une application (NdT : les réglages par défaut par exemple) (que cet état soit stocké dans des fichiers, dans des bases de données ou dans des cartes perforées) devrait être incorporée dans les objets “model” une fois la donnée chargée dans l’application.
Idéalement, un objet “model” n’a pas de connexion avec l’interface utilisateur permettant de le visualiser et de le modifier. Par exemple, si vous avez un objet représentant une personne (disons que vous écrivez une application de carnet d’adresses), vous pourriez vouloir stocker une date de naissance. Cela serait une bonne chose de la stocker dans votre objet “model” Personne. Poutant, le stockage d’une chaîne de caractères (string) au format Date (NdT : String = Un ensemble de caractères consécutifs) ou de toute autre information se rapportant à la manière de la présenter est probablement plus approprié ailleurs.
En pratique, cette séparation n’est pas toujours souhaitable, et il y a des marges de manœuvre, mais en général un objet “model” ne doit pas être concerné par des considérations d’interface ou de présentation. Un exemple où une exception est acceptable est une application de dessin dont des objets “model” représentent les graphiques affichés. Il est compréhensible que les objets graphiques sachent comment se dessiner eux-mêmes car leur principale raison d’être est de décrire un élément visuel. Mais, même dans ce cas, les objets graphiques ne doivent pas compter sur une vue particulière ni sur aucune autre vue, et ils ne doivent pas être chargés de savoir quand ils ont à se dessiner. C’est à l’objet “view” de leur demander de se dessiner lorsqu’il veut les présenter.
Les objets “view” présentent l’information à l’utilisateur
Un objet “view” sait comment afficher et éventuellement modifier les données du modèle d’application. L’objet “vue” n’est pas responsable du stockage des données qu’il affiche. (Cela ne signifie pas que l’objet “view” ne stocke jamais les données qu’il affiche. Un objet “view” peut mettre en cache les données ou faire d’autres choses similaires pour des raisons de performance). Un objet “view” peu être chargé d’afficher une partie d’un objet “model” ou un objet “model” entier ou bien encore plusieurs objets “model” différents. Les objets “View” sont de différentes sortes.
Les objets “view” tendent à être réutilisables et fournissent une homogénéité entre les applications. Dans Cocoa, l’Application Kit définit un grand nombre d’objets “view”. En réutilisant ces objets, comme le NSButton, vous garantissez que les boutons de votre application fonctionnent comme les boutons des autres applications Cocoa, permettant aux utilisateurs d’en connaître le fonctionnement avec un haut niveau de probabilité.
Un objet “view” assure que l’objet “model” est affiché correctement. En conséquence, il a besoin de connaître les changements que l’objet “model” subit. Comme les objets “model” ne sont pas liés directement au objets “view”, ils ont besoin d’une voie générique pour leur indiquer qu’ils ont changé. Enfin, ils peuvent aussi envoyer un NSNotification quand ils sont modifiés ou définir une autre façon de transmettre les notifications de changement aux objets “view”, habituellement via la couche “controller”.
Les objets “controller” lient les objets “model” et les objets “view”
Un objet “controller” agit comme un intermédiaire entre les objets “view” et “model” de l’application. Typiquement, les objets “controller” contiennent une logique spécifique à l’application. Les objets “controller” sont souvent chargés de vérifier que les objets “view” ont accès à l’objet “model” qu’elles ont besoin d’afficher et agissent souvent comme le tuyau par lequel les objets “view” apprennent les changements des objets “model”.
En confinant le code spécifique à l’application dans les objets “controller”, vous rendez les objets “model” et “view” plus génériques et réutilisables. les objets “controller” sont souvent les objets les moins réutilisables de l’application, mais c’est acceptable. Vous pouvez tout réutiliser et si vous laissez le code non-réutilisable dans la couche “controller”, vous aurez plus de chance de réutiliser les autres objets.
La couche “controller” contient la plupart du temps beaucoup de lignes de code. Afin de rendre plus gérable cette quantité de code, il est parfois utile de subdiviser la couche “controller” entre des objets “model-controller” et des objets “view-controller”.
Un objet “model-controller” est un “controller” qui communique avec la couche “model”. Il “contrôle” l’objet “model”; sa principale responsabilité est de gérer la couche “model” et de communiquer avec l’objets “view-controller”. Les méthodes qui s’appliquent à l’ensemble du “model” seront typiquement mises en œuvre dans un objet “model-controller”. L’architecture “document” fournit beaucoup de méthodes. Par exemple, NSDocument prend automatiquement en charge les méthodes concernant la sauvegarde de fichiers.
Un objet “view-controller” est un objet “controller” qui se préoccupe plus particulièrement de la couche “view”. Il “contrôle” l’interface (les objets “view”); Sa principale responsabilité est de gérer l’interface et de communiquer avec l’objet “model-controller”. Les méthodes concernées par les données affichées dans un objet “view” sont typiquement mises en œuvre dans un objet “view-controller”.
La section “Architecture Document” fournit un peu plus de détails sur la distinction entre les objets “model-controller” et “view-controller”.
Pourquoi le modèle MVC est-il important ?
Apple ajoute beaucoup de nouvelles fonctionnalités aux frameworks Cocoa. Quelques fonctionnalités, comme le scripting et l’annulation d’actions (”undo”), sont de haut niveau comparées à ce que fournissaient les frameworks Application Kit et Foundation auparavant. Comme de plus en plus de fonctionnalités de haut niveau sont ajoutées, la probabilité est de plus en plus grande que les applications utilisant ces fonctionnalités soient basées sur des schémas de haut niveau, comme le MVC. En tant que développeur, vous devez plus vous impliquer dans la conception d’application en relation avec ces fonctionnalités.
Bien que les développeurs aient toujours été encouragés à utiliser le modèle MVC, avec l’arrivée de nouvelles fonctionnalités comme l’architecture document, le support de l’annulation, et le scripting, il est plus important que jamais pour les concepteurs d’applications de prendre à cœur le schéma MVC. Toutes les nouvelles fonctionnalités de haut niveau fonctionneront mieux si le schéma MVC est respecté. Il devrait être moins difficile d’utiliser ces nouvelles fonctionnalités si votre application a une bonne séparation entre les couches “model”, “view” et “controller”, alors que ce sera plus difficile dans le cas contraire.
(L’Application Kit est le plus souvent un framework pour la couche “view”, bien que certaines des nouvelles classes commencent à empiéter sur la couche “controller” et qu’il y a ausi quelques objets “model” dedans. Le framework Foundation contient surtout des objets “model” et quelques objets “controller”).
L’architecture document
Résumé
L’architecture document est basée sur trois classes dans “l’Application Kit” : NSDocument, NSWindowController, et NSDocumentController. NSDocument est la classe principale. Elle représente un unique document dans votre application. Les développeurs doivent créer une sous-classe de NSDocument pour lui donner connaissance de la couche “model” de l’application et pour mettre en oeuvre la persistance (chargement et sauvegarde). Les NSWindowController possèdent et dirigent l’interface utilisateur de l’application. Un NSDocument a un ou plusieurs NSWindowControllers. Les développeurs créent souvent des sous-classes de NSWindowController pour ajouter des connaissances spécifiques concernant la couche “view” que le “controller” gère. NSDocumentController est une classe qui doit être unique. Chaque application basée sur des documents possède un seul NSDocumentController pour suivre et gérer tous les documents ouverts. Les développeurs n’ont généralement pas besoin de créer des sous-classes de NSDocumentController.
Les NSDocuments sont des “Model-Controllers”
NSDocument une classe de type “model-controller”. Son rôle principal est de posséder et diriger le modèle qui décrit un document et de procurer un moyen de sauvegarder un fichier et de le recharger plus tard. Tout les objets qui font partie de l’état permanent d’un document doit être considérer comme faisant partie du modèle de ce document. Parfois le NSDocument lui-même contient quelques données qui peuvent être considérées comme faisant partie du modèle. Par exemple, l’application Sketch contient une sous-classe de NSDocument nommée SKTDrawDocument; les objets de cette classe peuvent contenir un tableau d’objets SKTGraphic qui constitue le modèle de document. En plus des objets SKTGraphic, l’objet SKTDrawDocument contient des données qui peuvent être techniquement considérées comme faisant partie du modèle car l’ordre des graphiques dans le tableau du document sert à déterminer l’ordre des SKTGraphics en perspective.
Un NSDocument ne devrait ni contenir ni exiger la présence de tous objets qui soit spécifique à l’interface utilisateur de l’application. Bien qu’un document puisse posséder et diriger des objets NSWindowController—lesquels affichent le document et permettent à l’utilisateur de le modifier—il ne devrait pas compter sur ces objets. Par exemple, il pourrait être intéressant d’avoir un document ouvert dans votre application sans qu’il soit affiché. Par exemple, un script pourrait avoir ouvert un document pour lancer une opération dessus. Si le script n’a pas besoin de l’utilisateur pour lancer le processus, il peut vouloir ouvrir un document, le manipuler, le sauvegarder , et encore le fermer, sans que quoi que ce soit apparaisse à l’écran.
Les NSWindowControllers sont des “View-Controllers”
NSWindowController est une classe de type “view-controller”. Son rôle principale est de posséder et diriger les objets “view” qui sont utilisés pour afficher et modifier un document. Un document visible pour l’utilisateur a un ou plusieurs NSWindowControllers pour contrôler la présentation à l’?cran. Bien que vous puissiez utiliser une instance de NSWindowController, vous devez le plus souvent créer une sous-classe de NSWindowController pour ajouter des spécificités de l’interface. Un NSWindowController obtient son interface d’un fichier nib. Les sous-classes ajoutent souvent des “outlets” (NDT : les résultats de processus internes au programme) et des “actions” (NDT : ce qui permet de lancer un processus interne) au programme pour les contrôles (par exemple un bouton) et les vues (par exemple un champ de texte) contenus dans le fichier nib et le NSWindowController agit habituellement en tant que “propriétaire” (NDT : au sens UNIX du terme) du fichier nib.
Dans les cas très simples où il n’y a qu’une fenêtre pour un document, vous pouvez vouloir que votre classe NSDocument ait ses “outlets” et “actions” pour le fichier nib. Dans ce cas, la sous-classe de NSDocument agit en tant que “propiétaire” du fichier nib, mais elle crée encore un NSWindowController pour posséder et diriger les objets chargés depuis le fichier nib. Si vous décidez d’adopter cette approche quand vous créez rapidement un prototype d’application, vous devez être attentif à la localisation des portions de code en relation avec l’interface utilisateur, ainsi vous pourrez les extraire plus tard du document et les placer dans un NSWindowController sur mesure quand votre application deviendra plus complexe.
Les NSDocumentControllers et le type des informations
Un objet NSDocumentController gère les documents. il garde une trace de chaque document ouvert; il sait comment créer de nouveaux documents et sait comment ouvrir ceux qui existent déjà. Il sait comment trouver les documents ouverts soit par ré,férence à une fenêtre que le “controller” associe à un document soit à partir du chemin par lequel a été chargé. Les développeurs n’ont habituellement pas à se préoccuper de ce qu’il fait. Le NSDocumentController sait comment lire et utiliser les “metadata” qu’une application basée sur des documents (NDT : document-based application) procure au sujet des types de documents qu’elle peut ouvrir. Un NSDocumentController peut produire des informations fondées sur ces “metadata”, comme des listes de types de fichiers supportés par l’application et le nom des sous-classes de NSDocument utilisées pour eux.
toutes les applications basées sur des documents déclarent des informations sur les types de documents qu’elles supporte dans le fichier Info.plist (NDT : pour “information property list”, que l’on peut éventuellement traduire par “liste des propriétés des informations”) de l’application. Project Builder procure un éditeur pour créer et modifier ces “metadata”. Allez voir les spécifications de la classe NSDocumentController pour plus de détails sur les éléments du fichier Info.plist requis par l’architecture document et comment les inclure dans votre projet.
Les “metadata” contenues dans le fichier Info.plist déclarent les types de documents supportés par une application. Cocoa définit un ensemble de types abstraits; ces types sont habituellement les mêmes que ceux du “presse-papier” qui représentent ces données. Pour chaque type abstrait, le fichier Info.plist liste des informations spécifiques telles que :
- L’extension utilisée pour identifier les fichiers ce type
- Les quatre lettres servant de code HFS aux fichiers de ce type
- L’icone que le Finder doit utiliser pour les fichiers de ce type
- La sous-classe de NSDocument utilisée par une application pour traiter les fichiers de ce type
Le NSDocumentController charge toutes ces informations et les utilisent. Quand le NSDocumentController fonctionne dans une fenêtre d’ouverture de fichier, il obtient la liste de toutes les extensions de fichiers pour les types de documents que votre application peut lire; il passe cette liste à la fenêtre d’ouverture de sorte qu’elle puisse énumérer les fichiers qui peuvent être ouverts. Quand l’utilisateur choisit un fichier à ouvrir, le NSDocumentController utilise les “metadata” pour identifier la sous-classe de NSDocument à utiliser pour créer le document et charger ses données.
Exemples typiques d’utilisation
Vous pouvez utiliser l’architecture document de trois façons générales. La discussion qui suit commence par la plus simple pour finir par la plus complexe.
La façon la plus simple d’utiliser l’architecture document est appropriée aux documents qui n’ont qu’une seule fenêtre et qui sont suffisamment simples pour qu’il n’y ait pas de bénéfice dans la séparation de la couche “controlleur” entre un “model-controller” et un “view-controller”. Dans ce cas, le développeur a seulement besoin de créer une sous-classe de NSDocument. La sous-classe de NSDocument procure un lieu de stockage pour le modèle et la faculté de charger et de sauver les données du document. Elle a aussi tous les “outlets” et “actions” requis pour l’interface utilisateur. Elle surpasse windowNibName pour retourner le nom du fichier nib utilisé pour les documents de ce type. NSDocument crée automatiquement un NSWindowController pour gérer ce fichier nib, mais le NSDocument lui-même sert de “propriétaire” au fichier nib.
Si votre document n’a qu’une fenêtre mais que qu’il est assez complexe pour que vous vouliez séparer une partie de la logique de la couche “controller”, vous pouvez créer une sous-classe du NSWindowController ou du NSDocument. Dans ce cas, tous les “outlets” et “actions” et autres comportement spécifique à la gestion de l’interface utilisateur va dans la sous-classe NSWindowController. Votre sous-classe NSDocument doit surpasser makeWindowControllers au lieu de windowNibName. La méthode makeWindowControllers crée une instance de votre sous-classe de NSWindowController et l’ajoute à la liste des fenêtre gérées par le contrôleur de fenêtre avec addWindowController:. Le NSWindowController doit être le propriétaire du fichier nib car cela crée une meilleur séparation entre les instructions liées à la couche “view” et celles liées à la couche “model”. Cette approche est recommendée dans tous les cas sauf les plus simples.
Si votre document requièrt plusieurs fenêtres (ou autorise de multiples fenêtres) pour un seul document vous devez créer des sous-classes de NSWindowController et de NSDocument. Dans votre sous-classe de NSDocument, vous surpassez seulement makeWindowControllers comme dans la seconde procédure décrite ci-dessus. Cependant, dans ce cas vous devriez créer plus d’une instance de NSWindowController, probablement de différentes sous-classe de NSWindowController. Certaines applications ont besoin de plusieurs fenêtres pour reprér un document. Donc vous avez certainement besoin de plusieurs sous-classes différentes de NSWindowController et vous devez créer une de chaque avec makeWindowControllers. Certaines applications n’ont besoin que d’une fenêtre pour un document mais veulent autoriser l’utilisateur à créer plusieurs copies de cette fenêtre (quelques fois appelé document multivue) de telle façon que l’utilisateur puisse avoir chaque fenêtre placée à une position différente, ou affichée d’une manière différente. Dans ce cas, votre makeWindowControllers ne devra créer qu’un NSWindowController, mais il y aura une commande de menu ou un controle similaire qui autorisera l’utilisateur à en créer d’autres.
Les documents et le scripting
Le support du scripting est plus automatique dans les applications basées sur la nouvelle architecture document, pour plusieurs raisons. Premièrement, NSDocument et les autres classes de l’architecture document mettent en oeuvre directement les classes standards de scripting (dans le sens AppleScript) et supportent automatiquement la plupart des commandes qui s’appliquent aux documents. Deuxièmement, comme l’architecture document est supposée fonctionner avec des applications conçues en utilisant la séparation “MVC”, et comme le support du scripting dépends de beaucoup de points communs de conception, les applications qui utilisent l’architecture document sont déjà dans un meilleur état pour supporter le scripting que d’autres applications qui ne sont pas conçues de cette manière. Enfin, le document joue un rôle important dans l’API de scripting de beaucoup d’applications; NSDocument sait commentremplir ce rôle et procure un bon point de départ pour autoriser l’accès par des scripts à la couche “model” de votre application.
Si une application n’est pas basée sur l’architecture document, la rendre scriptable requièrt que vous dupliquiez le travail que vous auriez eu autrement sans rien faire. Le projet d’application TextEdit (distribuée avec MacOS X) montre comment créer une application basée sur les documents qui n’est pas basée sur un NSDocument scriptable. Voyez le projet Sketch pour avoir un exemple de la mise en oeuvre d’une application basée sur un NSDocument scriptable.
Le Scripting
Cette section décrit le support du scripting dans Cocoa. Si vous êtes débutant en Applescript et en scripting, vous devriez d’abord lire (NdT : en anglais) “AppleScript on Mac OS X” . (NdT : Une série est aussi consacrée à AppleScript sur Project:Omega).
Le scripting et la couche “model”
Le support du scripting dans les frameworks Cocoa est construit pour rendre facile sa mise en œuvre dans les objets de la couche “model” d’une application. AppleScript n’a jamais encouragé le scripting de l’interface utilisateur d’une application car la plupart du temps, la façon la plus efficace de faire quelque chose pour un script n’est pas la façon qu’aurait un utilisateur de le faire avec la même application. Si le scripting ne servait qu’à procurer un accès à l’interface utilisateur, il ne servirait qu’à améliorer le quotidien.
Les scripts fonctionnant avec les objets de la couche “model” sont comme des processus “batch” (NdT : traduction approximative : “groupe de tâches enregistrées et exécutées ensembles à la demande”). Ils accomplissent leurs opérations et n’ont pas besoin ni ne veulent de la participation de l’utilisateur. Exemple d’un tel script “batch” : un script extrait des données d’une base de données, agit avec plusieurs applications, puis envoie le tout à un programme de mise en page pour générer les pages de pubs d’un journal. Le but de ce type de processus n’est pas de faire participer l’utilisateur, mais d’aller directement aux objets de la couche “model” afin de faire le travail.
Parfois, toutefois, vous pourriez avoir envie de changer certains aspects de l’interface utilisateur pendant l’exécution du script. Les scripts travaillant avec les objets de la couche “view” sont comme des macros. Ils font une manipulation très spécifique dans une application, habituellement une assez petite et d’un seul bloc, et leur propos est l’automatisation de petites et répétitives tâches pour l’utilisateur. Par exemple, un script qui prend le dessin sélectionné dans un logiciel de mise en page, place une légende en dessous, et place des lignes bleues le long des bords externes du groupe résultant pour faciliter l’alignement, pourrait être un script de cette sorte.
Un tel script est comme une macro car l’utilisateur fait une petite préparation (comme sélectionner le dessin), invoque le script, puis continue quand c’est fait. Pour ce type de script, votre application doit rendre scriptable certaines de ses structures d’interface —par exemple, vous pourriez avoir besoin de rendre scriptables les fenêtres et les sélections. Rendre ces structures scriptables peut de toute manière être complémentaire du support que vous proposer au niveau des objets de la couche “model”.
Il existe certaines techniques de programmation qui empêchent la conception d’application scriptable au niveau des objets de la couche “model”. Beaucoup d’applications simples enregistrent leur état dans la couche “view” (c’est à dire dans un objet de l’interface utilisateur). Par exemple, un panneau de contrôle des préférences (Preference panel) pourrait être construit de telle manière qu’un attribut de type booléen soit “stocké” dans une “case à cocher” dans le panneau de préférence et qu’il soit retrouvé et initialisé grâce aux méthodes state and setState:. De toute manière, conserver des état dans des objet de la couche “view” n’est généralement pas une bonne stratégie pour les données qui font partie d’un modèle de document (NdT : voir Architecture Document) car c’est contraire au modèle de conception MVC. Si un script a besoin d’être capable d’avoir accès et de modifier l’état, l’état doit être séparé de la couche “view” et être stocké dans un objet de la couche “model” ou, s’il n’appartient pas à la couche “model”, dans un objet de la couche “controller”. Souvent, cette séparation est nécessaire ou souhaitable même en dehors de toute considération sur la scriptabilité de l’application.
Par exemple, si un panneau de préférences stocke les paramètres courants seulement dans les contrôles du panneau, il ne peut répondre à aucune question au sujet de ceux-ci sans charger le panneau. Si d’autres parties de l’application ont besoin des préférences même si l’utilisateur n’a pas ouvert le panneau de préférences (une situation probable), alors cela sera mieux si les paramètres sont stockés dans le “controller” lui-même. Cela permet d’éviter d’avoir à charger un fichier nib (une activité très gourmande) tant que cela n’est pas vraiment nécessaire.
Le même argument est valable pour des comportements plus simples. Par exemple, dans un panneau de recherche, au lieu de mettre en œuvre le processus pour effectuer réellement l’action “trouver” dans la méthode “action” appelée par le bouton “Trouver suivant”, vous pourriez probablement définir une API dans votre classe document ou dans vos objets de la couche “model” qui serait capable d’exécuter la recherche. Le bouton “Trouver suivant” appellerait alors cette API. L’avantage de ce schéma est que quand vous voulez que des scripts puissent chercher des documents, vous pouvez faire en sorte que le script utilise l’API au lieu de lui demander d’utiliser le panneau de recherche.
Le scripting et le codage par valeur clef
Le scripting sur MacOS X dépend beaucoup du codage par valeur clef pour fournir le support automatique de l’exécution de commandes Applescript. Dans le codage par valeur clef, chaque objet de la couche “model” défini un ensemble de clefs qu’il supporte. Une clef représente une partie spécifique des données qui appartiennent à l’objet. Quelques exemples de clefs liées au scripting : “words”, “font”, “documents” et “color”.
L’API de codage par valeur clef fournit un moyen générique et automatique pour demander à un objet la valeur de ses clefs et pour affecter de nouvelles valeurs à ces clefs. Les méthodes primitives pour le codage par valeur clef sont valueForKey: et takeValue:forKey:. NSObject a des implémentations génériques de ces méthodes qui semblent à première vue utiliser les méthodes standards d’accès set et get basées sur les clefs (telles que getColor et setColor: pour la clef nommée “color”). Si la classe de l’objet n’implémente pas de méthode d’accès, le codage par valeur clef affecte ou lit directement la valeur de la variable d’instance (”color”). le codage par valeur clef définit beaucoup d’autres méthodes étendues qui sont implémentées en terme de deux primitives, mais elles ne sont pas traitées ici car elles n’ont que peu d’intérêt pour l’implémentation du support du scripting.
Comme vous consever les objets de votre application, vous devez définir l’ensemble des clefs de vos objets de la couche “model” et implémenter les méthodes d’accès. Ensuite, quand vous definissez les suites de scripts (suites au sens Applescript du terme) de votre application, vous pouvez spécifier les clefs admises par chaque classe scriptable. Si vous mettez en oeuvre le codage par valeur clef, vous aurez “gratuitement” beaucoup d’outils pour le support du scripting.
Les clefs sont de trois catégories, lesquels ont leurs racines dans les bases de données relationnelles. Les clefs sont soit des clefs d’attributs (par exemple “color”) soit des clefs de relation “un-à-un” (un objet NSTextStorage d’un document => un objet donné appartient à un document et à un seul) soit une clef de relation “un-à-plusieurs” (les documents d’une application => une application est en relation à plusieurs documents, et théoriquement par l’inverse). Ce classement a un sens dans d’autres contextes que les bases de données relationnelles, y compris le scripting. En language Applescript, ces types de clefs colle clairement aux propriétés et aux éléments. Les éléments correspondent aux clefs de relation (sans distinction entre les relations “un-à-un” et “un-à-plusieurs”) et les propriétés correspondent aux clefs d’attributs.
Alors pourquoi le codage par valeur clef est-il si important pour le scripting? Dans Applescript, “les hiérarchies objet” définissent la structure des objets de la couche “model” d’une application. Par exemple, una application de dessin a des documents et ces documents ont des objets graphiques. Les objets graphiques en fonction ont une profondeur de couleur et un trait de contour. La plupart des commandes Applescript spécifient un ou plusieurs objets de votre application en exposant cette hiérarchie d’objets des conteneur parents aux éléments enfants.
Par exemple, certains graphiques peuvent être identifiés par la commande graphics 5 thru 7 of the document ‘MyDocument’ of application ‘MyDraw’. Il doit y avoir plusieurs manières de trouver ces graphiques afin qu’ils puissent être activés. Le codage par valeur clef rend cette recherche entièrement automatique. Une applcation contient la clef “documents”, qui est une relation “un-à-plusieurs” (du fait qu’une application peut ouvrir plusieurs documents). Chaque document a une clef “name” qui identifie le fichier correspondant. Pour trouver le document nommé MyDocument, le framework peut demander tous les documents d’une application et contrôler le nom de chacun des fichiers correspondant jusqu’à ce qu’il trouve celui dont le nom est MyDocument. Comme le codage par valeur clef définit une façon uniforme de demander la valeur d’une clef (valueForKey:), tout ce travail peut être effectué automatiquement sans effort supplémentaire pour le développeur. De la même manière, une fois que le système de scripting fondé sur le codage par valeur clef a trouvé le document, il obtient la clef “graphics” et a partir d’elle trouve les élément 5 à 7.
Si vous avez déjà une expérience de Applescript, vous savez que dans une application Carbon, le travail décrit à l’instant dépend de Object Support Library de MacOS. La version Cocoa de Object Support Library sait comment utiliser le codage par valeur clef pour évaluer les attributs d’un objet. Au lieu d’appeler spécifiquement la bibliothèque et passer par toutes sortes d’outils d’évaluation, le développeur Cocoa compte simplement sur le mécanisme de codage par valeur clef. Bien sûr, vous pouvez être plus directement impliqué dans l’évaluation si vous avez besoin de le faire pour des raisons de performance ou si votre schéma de scripting ne correspond pas assez à votre schéma interne pour qu’un support automatique fonctionne.
L’utilité du codage de clef-valeur ne s’arrête pas à l’évaluation d’objet-attribut. La plupart des commandes principales définies par Applescript ont des implémentations par défaut basées sur le codage par valeur clef dans Cocoa. Par exemple, les commandes GetData et SetData ne requièrent aucun code supplémentaire pour que vos objets les supportent si les classes de ces objets définissent correctement leurs clefs et si elles implémentent les méthodes d’accès standards. La même chose est vraie pour les commandes Move, Clone, Delete, Create, Count et Exists. La plupart des commandes de script ont été implémentées de manières générique avec le codage par valeur clef, de sorte que la plupart des objets de la couche “model” n’auront pas du tout à s’inquiéter de cela. Si votre classe de la couche “model” doit manipuler une commande particulière d’une manière spéciale, même si cette commande a une implémentation par défaut, elle peut le faire.
Les identificateurs d’objet
Une commande de script est une expression AppleScript comme les mots dont la couleur est rouge dans le quatrième paragraphe du document de premier plan de l’application “TextEdit”. Dans une application Cocoa, les éléments de cette commande sont représentés par des objets de la classe NSObjectSpecifier, qui utilise le codage par valeur clef pour évaluer les objets fondamentaux qu’ils représentent. les sous-classes de cette classe abstraite représentent les différentes formes de référence acceptées par AppleScript, telles que les références d’index (mot 5) et les références de filtre (tests ou clauses “whose”, comme les mots en rouge).
Les NSObjectSpecifiers peuvent être imbriquées, ainsi l’exemple du paragraphe précédent porrait être représenté par une chaîne de trois références : une pour les mots, une pour les paragraphes et une pour le document. (L’expression application ‘TextEdit’ n’a pas besoin de représentation parce que le spécificateur existe dans TextEdit avant que la commande soit exécutée.) Les NSObjectSpecifiers savent comment s’évaluer eux-mêmes dans leur spécificateurs les contenant. Le spécificateur explicite de haut niveau (document de premier plan dans notre exemple) s’évalue lui-même dans un conteneur de haut niveau par défaut, qui est habituellement l’application elle-même.
Vous ne devriez pas avoir besoin d’en connaître beaucoup au sujet des spécificateurs pour rendre une application scriptable, parce que le support du scripting intégré de Cocoa peut créer et résoudre automatiquement les spécificateurs. Toutefois, vous aurez besoin de savoir comment travailler avec eux si votre application a des besoins en scripting qui dépasssent les limitent de ce support intégré. Par exemple, les applications qui souhaitent supporter l’enregistrement auront besoin de créer des objets spécificateurs pour les actions enregistrées.
Les commandes de script
Quand un scripteur exécute un script qui envoie une commande (comme set the height of the first rectangle to 37) à une application, l’application reçoit une “Apple event” qui encapsule la commande. Le support intégré du scripting de Cocoa converti les “Apple events” en objets commandes de script basés sur la classe NSScriptCommand. Une application peut recevoir plusieurs commandes de script consécutives, mais chacune est séparée, distincte et complète.
Une commande de script peut être une instance de NSScriptCommand lui-même, mais Cocoa fournit aussi plusieures sous-classes de NSScriptCommand, dont les implémentations par défaut utilisent le codage par valeur clef pour manipuler les commades AppleScript standards comme Get Data, Set Data et d’autres. Une sous-classe peut aussi être nécessaire si une commande a des arguments qui nécessitent un traitement spécial pour être converti dans un format exploitable.
Un NSScriptCommand a un spécificateur d’objet qui identifie le ou les receveur(s) de la commande et peuvent avoir un autre spécificateur d’objet pour chaque argument définit par la commande. Les arguments de la commande peuvent être des valeurs réelles ou des spécificateurs d’objet qui identifient l’endroit où trouver les valeurs réelles dans la hiérarchie des objets de l’application.
Une classe scriptable déclare quelle commandes elle supporte. Pour les commades qui ont une implementation par défaut, les classes scriptables peuvent choisir de les utiliser ou elles peuvent choisir d’implémenter les comportements requis par les commandes elles-mêmes. Pour les commandes sans implémentation par défaut, les objets scriptables doivent implémenter et spécifier une méthode de manipulation de ces commandes.
Il peut sembler bizarre que les commandes de script soient separées des classes qui les supportent. Bien que cela diverge du style orienté objet, AppleScript est conçu pour avoir une petit nombre de commandes qui agissent sur un grand nombre d’objets. Ceci procure quelques avantages —par exemple, cela donne aux frameworks Cocoa la possibilité de supporter les implémentations par défaut des commades, si les commades sont assez génériques pour être implémentées par codage par valeur clef.
Les “suites”
AppleScript groupe des tonnes d’informations de scripting dans des “suites”. Une suite consiste en un ensemble de descriptions de classes, un ensemble de descriptions de commandes et un ensemble de descriptions des terminologies pour chaque “dialecte” supporté par Applescript. Dans Mac OS, les suites que supporte une application sont définient dans la ressource “aete” de l’application. Dans les frameworks Cocoa, les suites sont définient dans les listes de propriétés (property lists). Vous pouvez créer et examiner les “property lists” avec l’utilitaire Property List Editor, distribué avec Mac OS X.
Chaque framework, application ou bundle chargeable peut déclarer des suites de script. L’ensemble de suites qu’une application supporte est le résultat de la réunion de toutes les suites définient par l’application elle-même, les frameworks qui y sont liés et les bundles qu’elle a chargés dynamiquement. Les frameworks Cocoa déclarent deux suites et donc toute application scriptable les supportent automatiquement. Ce sont la suite Core et la suite Text. Ainsi, si vous donnez accès à un objet NSTextStorage à travers votre hiérarchie d’objet, cet objet NSTextStorage est complètement et automatiquement scriptable à travers la suite Text standard. Si votre application utilise l’architecture document de l’Application Kit (exposée auparavant), elle supporte automatiquement toute les commandes de la suite Core qui peuvent être appliquées aux documents.
La liste de propriétés qui décrit une suite contient toutes les informations au sujet des classes et des commandes de cette suite qui sont exigées par les frameworks de scripting. Pour les classes, cela inclut toutes les clefs supportées (attribut et relation) par la classe et leurs types. Cela inclut aussi toutes les commandes que la classe supporte (Aussi bien celles de la classe elle-même que les autres). Pour les commandes cela inclut le nombre et le type des arguments, si besoin, et le type des données retournées. La définition de suite inclut également l’information requise pour tracer les classes et les commandes aux codes de quatre-lettre appropriés employés pour structurer les données dans un Apple event représentant une commande de script.
En tant que développeur Cocoa, vous n’avez normalement pas à vous occuper directement des Apple events pour supporter le scripting car Cocoa convertit les Apple events en commandes de script. Toutefois, vous aurez à fournir les informations nécessaires pour tracer les classes, les commandes, les clefs et les informations relatives aux codes utilisés dans les Apple events.
En plus de la définition de suite, qui est une ressource indépendante du language, une terminologie de suite contient les informations sur la terminologie spécifique aux dialectes qui identifie le vocabulaire de scripting utilisé dans les différentes classes et commandes.
les définitions de suite et les terminologies de suite sont décriptes avec plus de détails dans Scriptable Applications .
Les “suites” intégrées
Les frameworks Cocoa définissent deux suites standards, la suite Core et la suite Text. En plus, les classes Cocoa implémentent le scripting pour ces suites standards de telle façon que, par exemple, l’objet NSTextStorage est complètement scriptable en utilisant la suite Text et les objets NSDocumentController et NSDocument supportent les commandes de la suite Core qui ont un intérêt pour les documents.
Les “suites” personnalisées
Toute application peut définir ses propres suites. Dans ces suites elles peuvent décrire de nouvelles classes de script et de nouvelles commandes de script.
Annuler et rétablir
Les frameworks Cocoa fournissent des outils pour implémenter “annuler et rétablir”. Les objets NSUndoManager sont responsables de la recherche des actions nécessaires à l’annulation des changements effectués sur un document. Le fonctionnement de base de l’architecture d’annulation est de demander d’abord au NSUndoManager comment annuler une action avant de l’exécuter. La principale API est basée sur “invocation”, donc si vous avez une méthode setColor:, elle envoit un message similaire à celui-ci avant d’affecter réellement la nouvelle couleur :
[[undoManager prepareWithInvocationTarget:self] setColor:oldColor]
Ce message crée un NSInvocation; si l’utilisateur choisit “Annuler”, cette “invocation” (de la méthode setColor: aveccomme paramètre l’ancienne couleur) est appelée. Comme les changement par annulation sont stockés dans une pile “redo”, si l’utilisateur choisit la commande “Rétablir”, les changements sont à nouveau effectués.
Comme beaucoup de changements mineurs peuvent être provoqués par une action au niveau utilisateur, tous les enregistrements d’annulation qui surviennent au cours d’un cycle de la boucle d’événements sont habituellement regroupés et sont rétablient tous ensembles. NSUndoManager a des méthodes qui vous permettent de contrôler de manière plus pointue le comportement du regroupement si besoin.
Annuler et l’architecture document
Si vous utilisez l’architecture document, certains aspects de la manipulation de l’annulation sont gérés automatiquement. Par défaut, chaque NSDocument a un NSUndoManager. (Si vous ne voulez pas que votre application supporte l’annulation, vous pouvez utiliser la méthode setHasUndoManager: de NSDocument pour empêcher la création du contrôleur d’annulation.) Vous pouvez utiliser la méthode setUndoManager: si vous avez besoin d’utiliser une sous-classe ou bien si vous avez besoin de changer le contrôleur d’annulation utilisé par le document.
Quand un NSDocument a un NSUndoManager, le document garde automatiquement son état de modification à jour en cherchant les notifications du contrôleur d’annulation qui lui dit quand les changements sont faits, annulés, ou rétablits. Dans ce cas, vous ne devriez jamais à avoir à appeler directement la méthode updateChangeCount: de NSDocument, puisqu’elle est appelée automatiquement au moment voulu.
La chose importante à se rappeler au sujet du support de l’annulation dans une application basée sur les documents est que tous les changements qui affectent l’état persistant du document doit être annulable. Avec une architecture d’annulation multi-niveau, cela est très important. Si il est possible de faire quelques changements non-annulables à un document, alors la chaîne de modifications que le NSUndoManager gère pour le document peut devenir contradictoire avec l’état du document. Par exemple, imaginez que vous avez un programme de dessin qui est capable d’annuler un redimensionnement mais pas une suppression. Si l’utilisateur sélectionne un dessin et le redimensionne, le NSUndoManager crée une “invocation” qui peut annuler ce redimensionnement. Maintenant, l’utilisateur effece le dessin (action non enregistrée pour une annulation). Si l’utilisateur essaie maintenant d’annuler, rien ne se passe car le dessin qui avait été redimensionné n’est plus là et l’annulation du redimensionnement ne peut avoir aucun effet visuel. Au pire, l’application pourrait “quitter inopinément” en essayant d’envoyer un message à un objet libéré. Donc, quand vous implémentez l’annulation, rappelez-vous que tout événement qui peut modifier un document doit être annulable.
Annuler et la couche “model”
Le code le plus important pour le support de l’annulation doit être dans la couche “model”. Chaque objet “model” de votre application devrait être capable d’enregistrer les “invocations” d’annulation pour toutes les méthodes primitives qui modifient l’objet.
Il est souvent utile de structurer les API de votre objet “model” pour constituer des méthodes primitives et des méthodes étendues. Des exemples de cette séparation peuvent être trouvés dans le framework Foundation (contenant NSString, NSArray, NSDictionary) et dans le projet-exemple Sketch.Si vous avez une séparation de cette sorte dans vos objets “model”, rappelez-vous que seules les méthodes primitives devraient être enregistrées pour l’annulation car, par définition, les méthodes étendues sont implémentées à partir des primitives.
Certaines situations peuvent requiérirent que vous interrompiez temporairement l’enregistrement de l’annulation pour certaines commandes. Par exemple, une application Sketch laisse l’utilisateur redimensionner un dessin en saisissant une marque de redimensionnement et en tirant dessus. Pendant cette action, des centaines, voire des milliers, de changements sont effectués sur les limites du dessin sélectionné. Changer les limites d’un dessin est une opération primitive et devrait être normallement enregistrée pour une annulation. Pendant que l’utilisateur est en train de redimensionner, cependant, ce serait mieux si ces centaines ou milliers d’enregistrements d’annulation n’avaient pas lieu. Dans ce cas votre objet “model” devrait fournir une API pour suspendre et résumer temporairement quelques uns ou bien tous ces enregistrements. C’est à vous de décider comment manipuler cela. Cela fonctionnerait certainement si ces milliers d’enregistrements étaient faits, mais ce serait un formidable gachis en mémoire que d’avoir à ce remémorer tous ces changements intermédiaires alors que vous n’aurez jamais à restaurer un de ces états intermédiaires.
Annuler et les couches “control” et “view”
Bien que la plus importante partie du support de l’annulation devrait être dans la couche “model”, il y a deux situations dans lesquelles vous avez besoin de code relatif à l’annulation dans vos objets “controller” ou “view”. La première est quand vous voulez que les articles de menu “Annuler” et “Rétablir” aient des noms plus spécifiques. Vous pouvez utiliser la méthode setActionName: du NSUndoManager pour donner un nom au group d’annulation courant. La dernière “invocation” de setActionName: pendant un cycle d’événement est celui utilisé. Ces noms doivent reflèter l’intention de l’action de l’utilisateur, pas l’opération primitive résultat de l’action. Par conséquent, c’est dans vos méthodes d’action que vous devriez placer des noms d’action.
Il n’est pas absolument nécessaire de nommer un groupe d’annulation. Les articles de menu sont simplement “Annuler” et “Rétablir” sans aucune indication sur ce qu’il va être annulé ou rétabli. Mais quand vous enregistrez un nom, cela peut aider l’utilisateur à savoir ce qu’il va faire. Ce n’est pas très difficile de distiller quelques appel à setActionName: dans vos messages d’action de vos vues et contrôleurs.
Le second cas où vous pourriez avoir besoin de coder dans vos couches “view” et controller” est quand il y a des choses dont les changements n’affectent pas l’état actuel du document mais qui doivent pouvoir être annulés. Annuler l’action de sélectionner est souvent un de ces cas. Par exemple, l’application Sketch ne considère pas la sélection comme étant une partie du document. En fait, si le document peut avoir plusieures vues ouvertes, vous pouvez avoir différentes sélections dans chacune d’elles. Toutefois, vous pouvez vouloir que les changements de sélection puissent être annulés pour le bien-être des utilisateurs et pour obtenir une cohérence dans l’affichage quand l’utilisateur annule réellement quelquechose. Dans ce cas, la vue qui affiche le dessin peut garder une trace de la sélection. Elle doit enregistrer les “invocations” d’annulation pendant que la sélection change.
Les objets “controller” et “view” peuvent aller et venir pendant la durée de vie d’un objet document, et c’est à prendre en considération quand les événements des couches “controller” et “view” doivent pouvoir être annulés. Vos objets “model” sont tipyquement actifs pour la durée de vie du document et le document détient aussi le contrôleur d’annulation, ainsi vous n’avez en général pas besoin de vous préoccuper de ce qui se passe quand le “model” disparaît. Mais vous devrez vous préoccuper de ce qui arrive quand les objets “controller” et “view” disparaissent. Si votre objet “controller” ou “view” enregistre des “invocations” d’annulation, vous devez faire attention à bien les effacer du contrôleur d’annulation quand l’objet “controller” ou “view” est désaffecté. Vous pouvez utiliser la méthode removeAllActionsWithTarget: de NSUndoManager pour cela. Une fois qu’une vue particulière du document est fermée, il n’y a aucune raison de garder des informations d’annulation au sujet de choses telles que les changement de sélection de cette vue en particulier.
Annuler et le Scripting
Il est habituellement souhaitable de rendre annulable les changement par script. C’est une raison de plus de mettre votre support primaire de l’annulation dans vos objets “model”. Comme le scripting est habituellement dirigé vers la couche “model”, si votre support de l’annulation est dans vos primitives de la couche “model”, alors les actions de script pourront être annulées. La capacité d’annulation des actions scriptées est réellement très important avec les scripts de style macro, où le script est utilisé pour automatiser des tâches relativement petites qui sont entremélées avec les actions directes des utilisateurs. Spécialement dans ces cas, vous enregistrerez les changements par script en même temps que les actions directes des utilisateurs, et pour la même raison, il est important que tous les changements d’un document soient enregistrés. Si une application ne le fait pas, un document peut facilement devenir incohérent avec la pile d’annulation.
Fonctionnalités d’une application Cocoa
Une application Cocoa dispose des puissantes ressources des frameworks Cocoa, en particulier le “ApplicationKit”. Ces ressources logicielles—avec Project Builder, Interface Builder, et les autres outils de développement Cocoa—rendent possible le développement rapide d’applications puissantes et complètes.
Fonctionnalités “gratuites”
Les plus simple des applications Cocoa, même une sans une seule ligne de code ajoutée, comprend une panoplie de fonctionnalités que vous avez pour rien (”for free”). En d’autres termes, vous n’avez pas à programmer vous même ces fonctionnalités, ou bien leur programmation est triviale. Vous pouvez créer simplement un projet d’application avec Project Builder, créer une interface graphique avec Interface Builder, et construire l’application pour obtenir les fonctionnalités suivantes :
- Gestion des fenêtre et intégration dans l’espace de travail. En réponse aux actions de l’utilisateur, une application s’occupe de la fermeture, de la miniaturisation, du redimentionnement et de l’ombre de ses fenêtres. En coordination avec le Finder, l’application manipule ses propres désactivations et réactivation, masquage et affichage de fenêtres et fait tous les rafraîchissements d’écran nécessaires.
- Manipulation des événements. L’application crée sont cycle d’événement et, en coordination avec le système de fenêtrage, reçoit et distribue les événements aux fenêtres et vues dans lesquelles les actions des utilisateurs se produisent. Beaucoup d’actions d’utilisateur sont manipulées automatiquement mais vous pouvez implémenter vos propres manipulations des événements.
- Gestion des menu. Comme avec les fenêtres d’une application, un menu d’une application est automatiquement affiché et effacé de la bar de menu quand une application est lancée, désactivée, réactivée et quittée. L’application s’occupe de l’observation et de l’accentuation des articles de menu, de l’affichage des sous menus et des raccourcis claviers. Dans beaucoup de cas, une application déclenche des actions dans les objets prévus quand les articles de menu sont neutralisés et elle active ou neutralise les articles au moment opportun. Bien sûr, vous pouvez personnaliser le comportement du menu pour des besoins plus sophistiqués.
- Support du texte et des polices. Quand vous ajoutez les objets nécessaires à votre interface utilisateur avec Interface Builder, votre application reçoit automatiquement beaucoup de capacités relatives à l’édition de texte : menu de sélection de la police, de la taille et des styles et attributs de mise en page comme l’alignement, le kerning (ajustement des espaces entre les caractères, [Merci Sherlock's Dictionnary]) et les ligatures; un objet texte avec une règle, le défilement automatique et l’encadrement, le support intégré de l’affichage du texte simple, du RTF et du HTML (et l’écriture en texte simple et en RTF). (Bien que la plupart des fonctions sont directement accessibles, vous devez encore programmer pour fournir la possibilité de sauvegarder et lire les fichiers de texte).
- Comportement des contrôles. Les applications gèrent automatiquement l’accentuation des contrôles, l’affichage des curseurs, la coordination des boutons-radios, l’affichage les listes contextuelles, et beaucoup d’autres aspects de l’apparence et du comportement des contrôles. Vous pouvez personnaliser la plupart de ces aspects dans Interface Builder. Les applications gèrent aussi l’invocation des actions (méthodes) des objets cibles quand les contrôles sont activés (vous réglez ces associations dans Interface Builder). Vous pouvez créer vos propres contrôles personnalisés.
Autres fonctionnalités
Nombre d’autres fonctionnailités sont disponibles. Ces sujets sont abordés dans les “Related Topics” (http://developer.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/ProgrammingTopics/AppArchitecture/).

Texte original en anglais sur developer.apple.com : Overview of Programming Topic: Application Architecture
Chargement
Commentaires récents