You are on page 1of 451

Rfrence

Programmation

Cocoa
sous Mac OS X
Aaron Hillegass

Rseaux et tlcom Programmation

Gnie logiciel

Scurit Systme dexploitation

3e dition

Cocoa
3e dition

Aaron Hillegass

Pearson Education France a apport le plus grand soin la ralisation de ce livre an de vous fournir une information complte et able. Cependant, Pearson Education France nassume de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces personnes qui pourraient rsulter de cette utilisation. Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descriptions thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle. Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices ou dommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou programmes. Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs propritaires respectifs.

Publi par Pearson Education France 47 bis, rue des Vinaigriers 75010 PARIS Tl. : 01 72 74 90 00 www.pearson.fr Mise en pages : TyPAO ISBN : 978-2-7440-4093-1 Copyright 2009 Pearson Education France Tous droits rservs

Titre original : Cocoa Programming for Mac OS X, 3th edition Traduit de lamricain par Herv Soulard Relecture technique : Fabien Schwob ISBN original : 978-0-321-50361-9 Copyright 2008 Pearson Education, Inc. All rights reserved

Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du code de la proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans le respect des modalits prvues larticle L. 122-10 dudit code. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc.

Table des matires


Prface ............................................................................................................................... Remerciements .................................................................................................................. 1 Prsentation de Cocoa ................................................................................................. Historique ................................................................................................................... Outils .......................................................................................................................... Langage ...................................................................................................................... Objets, classes, mthodes et messages ....................................................................... Frameworks ................................................................................................................ Comment lire cet ouvrage .......................................................................................... Conventions typographiques ...................................................................................... Erreurs classiques ....................................................................................................... Comment apprendre ................................................................................................... 2 Premiers pas ................................................................................................................. Dans Xcode ................................................................................................................ Crer un nouveau projet .................................................................................. La fonction main ............................................................................................. Dans Interface Builder ................................................................................................ La fentre Library ........................................................................................... La fentre vierge .............................................................................................. Agencer linterface .......................................................................................... La fentre du chier nib .................................................................................. Crer une classe ............................................................................................... Crer une instance ........................................................................................... tablir les connexions ..................................................................................... Retour dans Xcode ..................................................................................................... Types et constantes en Objective-C ................................................................. Le chier den-tte .......................................................................................... Modier le chier dimplmentation .............................................................. Compiler et excuter ....................................................................................... La mthode awakeFromNib ............................................................................ Documentation ........................................................................................................... Travail ralis ............................................................................................................. XIII XIV 1 1 4 5 5 6 7 8 8 9 11 12 12 15 16 16 17 17 19 19 21 22 24 25 26 27 28 30 31 31

IV

Table des matires

3 Objective-C .................................................................................................................. Crer et utiliser des instances ..................................................................................... Utiliser les classes existantes ...................................................................................... Envoyer des messages nil ............................................................................. NSObject, NSArray, NSMutableArray et NSString ......................................... "Hrite de", "Utilise" et "Connat" .................................................................. Crer ses propres classes ............................................................................................ Crer la classe LotteryEntry ............................................................................ Modier lottery.m ............................................................................................ Implmenter une mthode description ............................................................ Mthodes dinitialisation ................................................................................. Mthodes dinitialisation avec arguments ....................................................... Le dbogueur .............................................................................................................. Travail ralis ............................................................................................................. Pour les plus curieux : mcanisme des messages ....................................................... Exercice ...................................................................................................................... 4 Gestion de la mmoire ................................................................................................. Activer et dsactiver le ramasse-miettes ..................................................................... Programmer avec le ramasse-miettes ......................................................................... Programmer avec les compteurs de rfrences ........................................................... Implmenter dealloc ........................................................................................ Crer des objets libration automatique ....................................................... Mthodes accesseurs ....................................................................................... Travail ralis ............................................................................................................. 5 Cible et action .............................................................................................................. Principales sous-classes de NSControl ....................................................................... NSButton ......................................................................................................... NSSlider ........................................................................................................... NSTextField ..................................................................................................... Dbuter lexemple SpeakLine .................................................................................... Organiser le chier nib ............................................................................................... tablir des connexions dans Interface Builder ................................................ Outlet initialFirstResponder de NSWindow .................................................... Implmenter la classe AppController ......................................................................... Pour les plus curieux : xer la cible par programmation ............................................ Exercice ...................................................................................................................... Conseils de dbogage ................................................................................................. 6 Objets assistants .......................................................................................................... Dlgation ................................................................................................................... NSTableView et sa source de donnes ........................................................................

35 36 37 42 43 48 48 49 51 52 57 58 60 64 64 66 67 69 70 71 73 74 76 79 81 84 84 85 85 87 88 89 90 91 92 92 93 97 98 101

Table des matires

Agencer linterface utilisateur .................................................................................... tablir des connexions ................................................................................................ Modier AppController.m ........................................................................................... Erreurs classiques dans limplmentation dun dlgu ................................. Dlgus dobjets ............................................................................................ Pour les plus curieux : fonctionnement de la dlgation ............................................ Exercice : crer un dlgu ......................................................................................... Exercice : crer une source de donnes ...................................................................... 7 Modles de conception KVC et KVO ........................................................................ KVC .......................................................................................................................... Liaisons ....................................................................................................................... KVO .......................................................................................................................... Crer des cls observables .......................................................................................... Proprits et leurs attributs ......................................................................................... @property et @synthesize ............................................................................... Attributs dune proprit ................................................................................. Pour les plus curieux : chemin de cl ......................................................................... Pour les plus curieux : KVO ....................................................................................... 8 NSArrayController ...................................................................................................... Dbuter lapplication RaiseMan ................................................................................. Dans Xcode ..................................................................................................... Dans Interface Builder .................................................................................... Codage cl-valeur et nil .............................................................................................. Ajouter le tri ............................................................................................................... Pour les plus curieux : trier sans NSArrayController ................................................. Exercice 1 ................................................................................................................... Exercice 2 ................................................................................................................... 9 NSUndoManager ......................................................................................................... NSInvocation .............................................................................................................. Fonctionnement de NSUndoManager ........................................................................ Ajouter lannulation RaiseMan ................................................................................ Observation cl-valeur ................................................................................................ Annuler les modications ........................................................................................... Commencer la modication lors de lajout ................................................................ Pour les plus curieux : fentres et gestionnaire dannulation ..................................... 10 Archivage .................................................................................................................... NSCoder et NSCoding ................................................................................................ Encoder ........................................................................................................... Dcoder ...........................................................................................................

104 105 106 108 108 109 110 111 113 114 116 117 118 120 120 121 122 123 125 126 127 129 133 134 135 136 136 139 139 140 142 146 146 149 151 153 154 155 156

VI

Table des matires

Larchitecture de document ........................................................................................ Info.plist et NSDocumentController ................................................................ NSDocument .................................................................................................... NSWindowController ...................................................................................... Enregistrer et NSKeyedArchiver ................................................................................ Charger et NSKeyedUnarchiver .................................................................................. Affecter une extension et une icne au type de chier ............................................... Pour les plus curieux : empcher les boucles innies ................................................ Pour les plus curieux : crer un protocole .................................................................. Pour les plus curieux : applications bases sur des documents sans annulation ........ Identiants de type universel ...................................................................................... 11 Bases de Core Data .................................................................................................... NSManagedObjectModel ............................................................................................ Interface ...................................................................................................................... Crer et congurer des vues ............................................................................ Connexions et liaisons ..................................................................................... Fonctionnement de Core Data .................................................................................... 12 Fichiers nib et NSWindowController ...................................................................... NSPanel ...................................................................................................................... Ajouter un panneau lapplication ............................................................................. Dnir lentre de menu .................................................................................. AppController.m .............................................................................................. Preferences.nib ................................................................................................ PreferenceController.m ................................................................................... Pour les plus curieux : NSBundle ............................................................................... Exercice ...................................................................................................................... 13 Valeurs par dfaut de lutilisateur ........................................................................... NSDictionary et NSMutableDictionary ...................................................................... NSDictionary ................................................................................................... NSMutableDictionary ..................................................................................... NSUserDefaults .......................................................................................................... Priorit des diffrentes valeurs par dfaut ....................................................... Fixer lidentiant de lapplication .............................................................................. Crer des cls pour les valeurs par dfaut .................................................................. Inscrire les valeurs par dfaut ..................................................................................... Laisser lutilisateur modier les valeurs par dfaut ....................................................

157 158 158 161 161 162 163 166 167 168 168 171 171 174 174 177 181 183 184 184 186 188 189 193 195 196 197 198 199 200 200 202 202 202 203 204

Table des matires

VII

Utiliser les valeurs par dfaut ..................................................................................... Supprimer la cration des documents sans titre .............................................. Fixer la couleur darrire-plan de la vue tableau ............................................. Pour les plus curieux : NSUserDefaultsController ..................................................... Pour les plus curieux : lire et crire les valeurs par dfaut partir de la ligne de commande ................................................................................ Exercice ...................................................................................................................... 14 Noti cations ............................................................................................................... Ce que sont les notications ....................................................................................... Ce que ne sont pas les notications ............................................................................ NSNotication et NSNoticationCenter ..................................................................... Poster une notication ................................................................................................ Inscrire un observateur ............................................................................................... Traiter la notication .................................................................................................. Le dictionnaire userInfo ............................................................................................. Pour les plus curieux : dlgus et notications ......................................................... Exercice ...................................................................................................................... 15 Panneaux dalerte ...................................................................................................... Conrmer une suppression ......................................................................................... Exercice ...................................................................................................................... 16 Localisation ................................................................................................................ Localiser un chier nib ............................................................................................... Tables de chanes ........................................................................................................ Crer des tables de chanes ............................................................................. Utiliser la table de chanes .............................................................................. Pour les plus curieux : ibtool ...................................................................................... Pour les plus curieux : ordre explicite des options dans les chanes de format .......... 17 Vues personnalises ................................................................................................... La hirarchie des vues ................................................................................................ Dessiner une vue ......................................................................................................... Crer une instance dune sous-classe de vue .................................................. Inspecteur Size ................................................................................................. drawRect: ........................................................................................................ Dessiner avec NSBezierPath ....................................................................................... NSScrollView .............................................................................................................. Crer des vues par programmation ............................................................................. Pour les plus curieux : cellules ................................................................................... Pour les plus curieux : isFlipped ................................................................................ Exercice ......................................................................................................................

205 205 206 207 207 208 209 209 210 210 213 213 214 214 215 216 217 218 220 221 222 224 225 226 227 228 229 230 231 232 233 234 236 238 240 240 242 243

VIII

Table des matires

18 Images et vnements de la souris ............................................................................ NSResponder .............................................................................................................. NSEvent ...................................................................................................................... Recevoir les vnements de la souris ......................................................................... Utiliser NSOpenPanel ................................................................................................. Modier le chier nib ...................................................................................... Modier le code .............................................................................................. Intgrer une image dans la vue ................................................................................... Systme de coordonnes dune vue ............................................................................ Autodlement ........................................................................................................... Pour les plus curieux : NSImage ................................................................................. Exercice ...................................................................................................................... 19 vnements du clavier .............................................................................................. NSResponder .............................................................................................................. NSEvent ...................................................................................................................... Nouveau projet avec une vue personnalise ............................................................... Agencer linterface .......................................................................................... tablir les connexions ..................................................................................... crire le code ................................................................................................... Pour les plus curieux : effets de survol ....................................................................... La bote bleue oue .................................................................................................... 20 Attributs de texte ....................................................................................................... NSFont ........................................................................................................................ NSAttributedString ..................................................................................................... Afcher des chanes et des chanes avec attributs ...................................................... Afcher les lettres ....................................................................................................... Gnrer des donnes PDF partir de la vue ............................................................... Pour les plus curieux : NSFontManager ..................................................................... Exercice 1 ................................................................................................................... Exercice 2 ................................................................................................................... 21 Presse-papiers et actions sur nil ............................................................................... NSPasteboard ............................................................................................................. Ajouter le couper, copier et coller BigLetterView .................................................... Actions sur nil ............................................................................................................. Analyser la chane des rpondeurs .................................................................. Examiner le chier nib .................................................................................... Pour les plus curieux : quel objet envoie le message daction ? ................................. Pour les plus curieux : copie paresseuse ..................................................................... Exercice 1 ................................................................................................................... Exercice 2 ...................................................................................................................

245 245 246 247 248 249 252 253 254 257 257 258 259 261 261 262 262 263 266 270 271 273 274 274 277 277 279 281 281 282 283 284 285 286 287 288 289 289 291 291

Table des matires

IX

22 Catgories ................................................................................................................... Ajouter une mthode NSString ................................................................................ Pour les plus curieux : dclarer des mthodes prives ............................................... Pour les plus curieux : dclarer des protocoles informels .......................................... 23 Glisser-dposer ........................................................................................................... BigLetterView en tant que source ............................................................................... BigLetterView en tant que destination ........................................................................ registerForDraggedTypes ................................................................................ Ajouter la mise en exergue .............................................................................. Implmenter les mthodes dune destination .................................................. Tester ............................................................................................................ Pour les plus curieux : masque des oprations ........................................................... 24 NSTimer ..................................................................................................................... Agencer linterface ..................................................................................................... tablir les connexions ................................................................................................. Implmenter AppController ........................................................................................ Pour les plus curieux : NSRunLoop ............................................................................ Exercice ...................................................................................................................... 25 Feuilles ........................................................................................................................ Ajouter une feuille ...................................................................................................... Ajouter les outlets et les actions ...................................................................... Agencer linterface .......................................................................................... Ajouter le code ................................................................................................ Pour les plus curieux : contextInfo ............................................................................. Pour les plus curieux : fentres modales .................................................................... 26 NSFormatter .............................................................................................................. Formateur de base ....................................................................................................... Crer ColorFormatter.h ................................................................................... Modier le chier nib ...................................................................................... NSColorList ..................................................................................................... Rechercher des sous-chanes dans des chanes ............................................... Implmenter les mthodes ............................................................................... Le dlgu de NSControl ............................................................................................ Vrier des chanes partielles ..................................................................................... Formateurs qui retournent des chanes avec attributs ................................................. 27 Impression .................................................................................................................. Grer la pagination ..................................................................................................... Pour les plus curieux : suis-je en train de dessiner lcran ? ................................... Exercice ......................................................................................................................

293 293 295 296 297 298 301 302 302 303 304 305 307 309 311 312 314 314 315 316 317 318 321 322 323 325 327 327 328 330 330 331 333 334 336 337 338 342 342

Table des matires

28 Services web ............................................................................................................... AmaZone .................................................................................................................... Agencer linterface ..................................................................................................... crire le code .............................................................................................................. Exercice : ajouter une vue web ................................................................................... 29 change de vues ......................................................................................................... Conception .................................................................................................................. Crer les bases ................................................................................................. Crer la classe ManagingViewController ........................................................ Crer les contrleurs de vues et leur chier nib .............................................. Ajouter lchange de vues dans MyDocument ................................................ Redimensionner la fentre .......................................................................................... 30 Relations Core Data .................................................................................................. Modier le modle ..................................................................................................... Crer des classes NSManagedObject personnalises ................................................. Employee ......................................................................................................... Department ...................................................................................................... Agencer linterface ..................................................................................................... DepartmentView.nib ........................................................................................ EmployeeView.nib ............................................................................................ vnements et nextResponder .................................................................................... 31 Ramasse-miettes ........................................................................................................ Types de donnes non objets ...................................................................................... Types primitifs de C ........................................................................................ Core Foundation .............................................................................................. Exemple ...................................................................................................................... Instruments ................................................................................................................. Pour les plus curieux : rfrences faibles ................................................................... Exercice : faire des btises .......................................................................................... 32 Bases de lanimation .................................................................................................. Crer un CALayer ....................................................................................................... Utiliser CALayer et CAAnimation .............................................................................. Supprimer des polynmes ............................................................................... Dplacer plusieurs calques simultanment ..................................................... Redimensionner et redessiner les calques ....................................................... CALayer ..........................................................................................................

343 344 345 347 351 353 354 354 355 356 358 360 363 364 365 365 366 367 367 369 371 373 374 374 375 375 381 383 383 385 386 389 390 391 392 392

Table des matires

XI

33 Application Cocoa/OpenGL simple ........................................................................... Utiliser NSOpenGLView ............................................................................................. crire lapplication ..................................................................................................... Agencer linterface .......................................................................................... crire le code ................................................................................................... 34 NSTask ........................................................................................................................ Multithread et multitraitement .................................................................................... ZIPspector .................................................................................................................. Lectures asynchrones .................................................................................................. iPing .......................................................................................................................... Exercice : chiers .tar et .tgz ...................................................................................... 35 Final ............................................................................................................................ Exercice ...................................................................................................................... Index ..................................................................................................................................

395 395 396 397 400 403 404 404 409 410 414 415 416 417

Prface
Si vous dveloppez des applications pour Mac, ou que vous souhaitiez le faire, cet ouvrage va vous apporter laide que vous recherchez. Sil ne couvre pas lintgralit du dveloppement sur Mac, vous y trouverez au moins 80 % des informations indispensables. Les 20 % restants, qui vous sont propres, existent dans la documentation disponible en ligne sur le site web dApple. Ce livre constitue donc une base. Il prsente le langage Objective-C et les principaux modles de conception de Cocoa. Il vous permettra de dbuter avec les trois outils de dveloppement les plus employs : Xcode, Interface Builder et Instruments. Aprs avoir lu ces pages, vous serez en mesure de comprendre et dexploiter la documentation en ligne fournie par Apple. travers les nombreux exemples de code, vous dcouvrirez les idiomes de la communaut Cocoa. En prsentant ces exemples de code, nous esprons que vous deviendrez non seulement un dveloppeur Cocoa, mais galement un dveloppeur qui a de la classe. Cette troisime dition se fonde sur les technologies apportes par Mac OS X 10.4 et 10.5, incluant notamment Xcode 3, Objective-C 2, Core Data, le ramasse-miettes et CoreAnimation. Cet ouvrage est destin aux programmeurs qui possdent dj quelques connaissances en programmation C et objet. Vous ntes pas suppos avoir une exprience du dveloppement sur Mac. Vous devez avoir accs un systme Mac OS X et aux outils du dveloppeur. Ces outils sont gratuits. Si vous avez achet un exemplaire de Mac OS X, ils sont disponibles sur le DVD-ROM. Vous les trouverez galement en tlchargement sur le site web Apple Developer Connection ladresse http://developer.apple.com/. Jai tent de rendre ce livre le plus utile possible, dfaut dtre indispensable. Si vous avez des suggestions damliorations, nhsitez pas me les transmettre.
Aaron Hillegass aaron@bignerdranch.com

Remerciements
De nombreuses personnes se sont impliques dans lcriture de cet ouvrage. Je veux les remercier pour leur aide. Leurs contributions ont permis darriver un niveau de qualit quil maurait t impossible datteindre seul. Tout dabord, je souhaite remercier les tudiants qui ont suivi les cours de programmation Cocoa au Big Nerd Ranch. Ils mont aid retirer des exercices et des explications toutes les aberrations que javais pu introduire. Leur curiosit ma incit complter le contenu et leur patience la rendu possible. En maidant enseigner et dvelopper du contenu au Ranch, Chris Campbell et Mark Fenoglio ont jou un rle important pour cette dition. Ils ont apport des nouveauts et ont dtect mes erreurs monumentales. Jai eu le grand honneur de travailler pendant plusieurs annes avec Kai Christiansen. Il ma appris de nombreuses choses sur Cocoa et lenseignement. Ensemble, nous avons rdig plusieurs cours sur OpenStep et WebObjects. Lcriture de cet ouvrage a t pour moi une suite toute naturelle ce travail. Si mes mains taient sur le clavier, la voix de Kai tait souvent lorigine du contenu de la page. Addison-Wesley a pris mon manuscrit et en a fait un livre. Cette maison ddition la mis dans des camions et a convaincu les libraires de le placer sur leurs tagres. Sans son aide, il ne serait encore quune pile de feuilles de papier sur mon bureau. Enn, mes remerciements vont ma famille. Une grande part des attentions qui auraient d aller ma femme, Michele, a t consacre la rdaction de cet ouvrage. Je me dois galement de remercier mes ls, Walden et Otto, pour leur patience et leur comprhension.

1
Prsentation de Cocoa
Au sommaire de ce chapitre
Historique Outils Langage Objets, classes, mthodes et messages Frameworks Comment lire cet ouvrage Conventions typographiques Erreurs classiques Comment apprendre

Historique
Lhistoire de Cocoa dbute de manire cocasse. Deux copains, prnomms tous deux Steve, crent une socit appele Apple Computer dans leur garage. Cette entreprise grandit rapidement, tel point quun cadre expriment, John Sculley, est embauch pour en devenir le directeur gnral. Suite quelques conits, John Sculley place Steve Jobs un poste qui lui retire tout contrle sur la socit. Steve Jobs quitte alors Apple pour crer une autre socit, NeXT Computer. NeXT recrute une petite quipe dingnieurs brillants. Elle cre un ordinateur, un systme dexploitation, une imprimante, une usine et un ensemble doutils de dveloppement. Tous ces lments ont plusieurs annes davance sur les technologies concurrentes. La foule est bahie et enthousiaste. Malheureusement, cette mme foule nachte pas lordinateur ou limprimante. En 1993, lusine est ferme et NeXT Computer, Inc., devient NeXT Software, Inc.

Cocoa

Le systme dexploitation et les outils de dveloppement restent en vente sous le nom NeXTSTEP. Si lutilisateur lambda na jamais entendu parler de NeXTSTEP, ce dernier est en revanche trs connu chez les scientiques, dans les banques dinvestissement et les services de renseignement. Ces socits dveloppent chaque semaine de nouvelles applications et voient en NeXTSTEP une solution qui leur permet dimplmenter leurs ides plus rapidement que nimporte quelle autre technologie. Quel est donc ce systme dexploitation ? NeXT a choisi dutiliser Unix comme base pour NeXTSTEP, en partant du code source dun Unix BSD provenant de luniversit de Berkeley, en Californie. Pourquoi Unix ? Ce systme est moins sujet aux dysfonctionnements que Microsoft Windows ou Mac OS et dispose de fonctions rseau plus labores et plus ables. Sous le nom de Darwin, Apple a rendu disponible le code source de la partie Unix de Mac OS X. Une communaut de dveloppeurs poursuit le travail damlioration de Darwin. Pour en savoir plus, allez sur le site http://macosforge.org/. NeXT a galement conu un serveur de fentres pour le systme dexploitation. Un serveur de fentres rcupre les vnements gnrs par lutilisateur et les dirige vers les applications. Une application renvoie ensuite des ordres de dessin vers le serveur de fentres an quil actualise ce que lutilisateur voit. Le serveur de fentres de NeXT prsente plusieurs aspects intressants, notamment celui dutiliser le mme code de trac pour les applications et limprimante. Ainsi, le programmeur ncrit le code de dessin quune seule fois et lutilise ensuite pour lafchage lcran ou limpression. lpoque de NeXTSTEP, les programmeurs crivaient du code qui gnrait du PostScript. Avec Mac OS X, les programmeurs crivent du code qui utilise le framework CoreGraphics (galement appel Quartz). Quartz peut gnrer ces graphiques lcran, les envoyer limprimante ou produire des donnes au format PDF (Portable Document Format). PDF est un standard ouvert cr par Adobe Corporation pour les graphiques vectoriels. Si vous avez dj utilis des machines Unix, vous connaissez certainement le serveur de fentres X Window (ou serveur X). Le serveur de fentres de Mac OS X est totalement diffrent, mais remplit la mme fonction que le serveur X : il rcupre les vnements de lutilisateur, les redirige vers les applications et place sur lcran les donnes produites par les applications. Le protocole X ne gre pas trs bien les polices de caractres avec anticrnelage ni la transparence. Cest lune des raisons pour lesquelles le serveur de fentres de Mac OS X parat bien meilleur quun serveur X Window. NeXTSTEP fournit un ensemble de bibliothques et doutils qui permettent aux programmeurs dexploiter le gestionnaire de fentres de manire trs lgante.

Chapitre 1

Prsentation de Cocoa

Ces bibliothques sont appeles frameworks. En 1993, les frameworks et les outils ont volu. Ils ont chang de nom, pour devenir OpenStep, puis Cocoa. La Figure 1.1 montre que le serveur de fentres et les applications sont des processus Unix. Cocoa permet aux applications de recevoir les vnements issus du serveur de fentres et deffectuer le trac lcran.
Figure 1.1
Utilisation de Cocoa.
vnements Serveur de fentres

trac

Cocoa Application

UNIX BSD 4.4 Micronoyau Mach

La programmation avec les frameworks se fait laide du langage Objective-C. Comme C++, Objective-C drive du langage de programmation C, pour en faire un langage orient objet. Contrairement C++, Objective-C est faiblement typ et extrmement puissant. Cependant, puissance rime avec responsabilit. Objective-C permet aux programmeurs dintroduire des erreurs ridicules. Ce langage est une extension trs simple de C et vous constaterez quil est trs facile apprendre. Les programmeurs ont apprci OpenStep. Il leur permettait de tester facilement de nouvelles ides. titre dexemple, Tim Berners-Lee a dvelopp les premiers navigateur et serveur web sur NeXTSTEP. Les analystes pouvaient crire du code et tester de nouveaux modles nanciers bien plus rapidement. Les scientiques pouvaient dvelopper les applications qui leur permettaient deffectuer leurs recherches. Ce quen faisaient les services de renseignement reste un mystre, mais ils ont achet des milliers dexemplaires dOpenStep. Les outils de dveloppement OpenStep taient tellement commodes quils ont t ports sur Solaris et Windows NT. Le systme dexploitation NeXTSTEP a t install sur pratiquement tous les processeurs rpandus : Intel, Motorola, PA-RISC de Hewlett-Packard et SPARC. Assez bizarrement, OpenStep ne fonctionnait pas sur Mac, jusqu la premire version de Mac OS X Server, appele Rhapsody et sortie en 1999. Pendant de nombreuses annes, Apple Computer a travaill au dveloppement dun systme dexploitation dont les caractristiques taient semblables celles de NeXTSTEP. Ce projet, connu sous le nom de Copland, tranait en longueur et Apple a nalement dcid de larrter, pour trouver la version suivante de Mac OS auprs dune autre socit. Aprs avoir tudi les systmes dexploitation existants, Apple a choisi

Cocoa

NeXTSTEP. La socit NeXT tant petite, Apple la simplement rachete en dcembre 1996. Quelle est ma place dans cette histoire ? Jcrivais du code pour les ordinateurs NeXT Wall Street, jusquau jour o NeXT ma embauch pour enseigner la programmation OpenStep dautres dveloppeurs. Jtais employ chez NeXT lorsque la socit a fusionn avec Apple. Jai donc form de nombreux ingnieurs dApple au dveloppement dapplications pour Mac OS X. Je ne travaille plus pour Apple et enseigne prsent la programmation Cocoa pour Big Nerd Ranch, Inc. NeXTSTEP est devenu Mac OS X. Il se fonde sur Unix. Vous disposez de tous les programmes Unix standard, comme le serveur web Apache, sur Mac OS X, qui est plus stable que Windows et Mac OS 9. Linterface utilisateur est spectaculaire. En tant que dveloppeur, vous allez apprcier Mac OS X car Cocoa vous permettra dcrire des applications compltes de manire trs efcace et lgante.

Outils
Vous apprcierez Cocoa, mais peut-tre pas immdiatement. Tout dabord, vous devez apprendre les bases. Commenons par les outils que nous utiliserons. Tous les outils ncessaires au dveloppement avec Cocoa font partie des Mac OS X Developer Tools, qui sont fournis gratuitement avec Mac OS X. Si ces outils du dveloppeur ajoutent une dizaine dapplications pratiques votre systme, vous en utiliserez principalement deux : Xcode et Interface Builder. Le compilateur C de GNU (gcc) sert compiler le code, tandis que le dbogueur de GNU (gdb) vous aidera trouver vos erreurs. Xcode gre toutes les ressources qui composent une application : code, image, son, etc. Lcriture du code se fait dans Xcode, qui peut compiler et lancer votre application. Depuis Xcode, il est galement possible dinvoquer et de contrler le dbogueur. Interface Builder est un concepteur dinterfaces graphiques. Il vous permet dagencer des fentres et de leur ajouter des widgets. Cependant, ses possibilits ne sarrtent pas l. Interface Builder permet au dveloppeur de crer des objets et de modier leurs attributs. La plupart de ces objets sont des lments dinterfaces graphiques, comme des boutons et des champs de saisie, mais certains seront des instances des classes que vous aurez cres.

Chapitre 1

Prsentation de Cocoa

Langage
Tous les exemples de ce livre sont crits en Objective-C. Ce langage est une extension simple et lgante de C. Si vous connaissez dj le langage C et un langage orient objet, comme Java ou C++, sa matrise ne vous prendra quenviron deux heures. Il est possible de dvelopper des applications Cocoa en Ruby ou Python. Cet ouvrage ne sintressera pas ces techniques, mais vous trouverez toutes les informations ncessaires sur le Web. Cependant, pour les comprendre, vous devez avoir une connaissance minimale dObjective-C. Objective-C a rcemment fait lobjet dune rvision majeure. Tout le code prsent dans ce livre se fonde sur Objective-C 2. Dans Objective-C 2.0, Apple a ajout un ramasse-miettes que vous pouvez choisir dactiver ou non. Le code propos fonctionne avec ou sans le ramasse-miettes. Le code Objective-C est compil par gcc, le compilateur C de GNU. Ce compilateur vous permet de mlanger librement du code C, du code C++ et du code Objective-C dans le mme chier. Le dbogueur de GNU, gdb, servira dnir des points darrt et examiner les variables pendant lexcution. Objective-C laisse une grande libert au programmeur, qui peut donc introduire de nombreuses erreurs et sera heureux de disposer dun dbogueur performant.

Objets, classes, mthodes et messages


Le dveloppement avec Cocoa se fait en utilisant des techniques orientes objet. Cette section revient rapidement sur les termes employs dans la programmation oriente objet. Si vous navez aucune connaissance dans ce domaine, commencez par lire louvrage The Objective-C Language, dont le chier PDF est disponible sur le site web dApple (http://developer.apple.com/documentation/Cocoa/Conceptual/ ObjectiveC/ObjC.pdf). Un objet peut tre compar une structure C : il occupe de la mmoire et contient des variables. Les variables dun objet sont appeles variables dinstance. Voici donc les premires questions que lon se pose gnralement concernant la manipulation des objets : comment allouer de lespace pour un objet ? Quelles sont les variables dinstance dun objet ? Comment dtruire un objet lorsquil est devenu inutile ? Certaines variables dinstance dun objet sont des pointeurs vers dautres objets. Ces pointeurs permettent lobjet de "connatre" un autre objet.

Cocoa

Les classes sont des structures qui crent des objets. Elles prcisent les variables dtenues par les objets et sont responsables de lallocation de la mmoire pour les objets. Nous disons quun objet est une instance de la classe qui le cre (voir Figure 1.2).
Figure 1.2
Cration dinstances partir de classes.
La classe joue le rle dune fabrique qui cre des instances de cette classe.

Toto

Toto

Toto

Un objet est plus labor quune structure, car des fonctions peuvent y tre associes. Ces fonctions sont appeles mthodes. Pour invoquer une mthode, il faut envoyer un message lobjet (voir Figure 1.3).
Figure 1.3
Invocation de mthodes par des messages.
Lenvoi d'un message dclenche linvocation dune mthode dans l'objet qui le reoit.

Toto

Frameworks
Un framework est un ensemble de classes prvues pour tre utilises ensemble. Autrement dit, les classes sont compiles et runies dans une bibliothque de code rutilisable. Toutes les ressources associes sont places dans un rpertoire avec la bibliothque. Le nom de ce rpertoire possde lextension .framework. Les frameworks intgrs votre systme se trouvent dans le rpertoire /System/Library/ Frameworks. Cocoa est constitu de trois frameworks : 1. Foundation. Chaque langage de programmation oriente objet a besoin de classes standard. Les classes implmentant les chanes, les dates, les listes, les threads et les minuteries se trouvent dans le framework Foundation.

Chapitre 1

Prsentation de Cocoa

2. AppKit. Tout ce qui se rapporte linterface utilisateur est fourni par le framework AppKit. Les classes implmentant les fentres, les boutons, les champs de saisie, les vnements et les dessins se trouvent dans AppKit. Ce framework est galement appel ApplicationKit. 3. Core Data. Core Data facilite lenregistrement des objets dans un chier, puis leur chargement en mmoire. Il sagit donc dun framework de persistance. Dautres frameworks se chargent de diffrents aspects, comme le chiffrement, QuickTime ou la gravure de CD, mais ce livre se concentre sur Foundation, AppKit et Core Data car ils sont les plus employs. Lorsque vous les matriserez, les autres frameworks seront plus accessibles. Vous pouvez galement crer vos propres frameworks partir de vos classes. En gnral, lorsque plusieurs classes sont utilises dans diffrentes applications, elles sont converties en un framework.

Comment lire cet ouvrage


En crivant ce livre, je me suis imagin en train de guider un ami parmi toutes les tapes qui lui permettraient de comprendre la programmation Cocoa. Cet ouvrage joue le rle de guide pour ces activits. Trs souvent, je vous demanderai de raliser quelque chose, puis jexpliquerai les dtails et la thorie. Si vous vous sentez perdu, poursuivez votre lecture. Normalement, laide recherche se trouvera un ou deux paragraphes plus loin. Si vous tes toujours bloqu, vous pouvez obtenir de laide sur le site web consacr ce livre : www.bignerdranch.com/products/. Vous y trouverez galement des errata, des conseils, des exemples, ainsi que les solutions des exercices. Chaque chapitre sera un guide qui vous accompagnera tout au long du processus de dveloppement dune application. Cependant, il ne sagit pas dun livre de recettes. Cet ouvrage enseigne des ides, tandis que les exercices montrent comment les mettre en uvre. Nhsitez pas exprimenter. Les frameworks Cocoa contiennent environ trois cents classes. Elles sont toutes documentes dans la rfrence en ligne (accessible par lintermdiaire du menu Help de Xcode). Les programmeurs Cocoa passent beaucoup de temps parcourir ces pages. Cependant, sans un minimum de connaissances sur Cocoa, il est difcile de trouver les rponses ses questions. Lorsquune nouvelle classe sera prsente dans cet ouvrage, recherchez-la dans la documentation de rfrence. Vous ne comprendrez peut-tre pas toutes les informations qui sy trouvent, mais cela vous permettra dapprcier la richesse des

Cocoa

frameworks. Lorsque vous aurez termin la lecture de ce livre, la rfrence deviendra votre guide. En gnral, Cocoa tient ses promesses : les choses classiques sont simples, les choses rares sont possibles. Si vous crivez de nombreuses lignes de code pour raliser une tche somme toute ordinaire, vous avez probablement emprunt la mauvaise voie.

Conventions typographiques
Voici les conventions typographiques utilises pour faciliter la lecture du contenu de cet ouvrage. Les noms des classes commencent toujours par une majuscule, dans une police chasse constante. Les noms des mthodes commencent par une minuscule et sont galement dans une police chasse constante tout comme les autres littraux, y compris les noms des variables dinstance et les noms de chiers. Par exemple, vous pourriez rencontrer les phrases "La classe NSObject dnit la mthode dealloc" ou "Dans MaClasse.m, xez la variable couleurPreferee nil".

Erreurs classiques
Pour avoir regard de nombreuses personnes travailler avec ce contenu, je sais que les mmes erreurs se rptent des centaines de fois. Plus prcisment, les erreurs demploi des majuscules et les connexions oublies sont les plus frquentes. Les erreurs demploi des majuscules se produisent car les langages C et Objective-C sont sensibles la casse. Le compilateur voit dans Toto et toto deux choses diffrentes. Si votre code ne compile pas, vriez que toutes les lettres majuscules et minuscules sont correctes. Lors de la cration dune application, vous utiliserez Interface Builder pour connecter des objets entre eux. Si vous oubliez des connexions, votre application pourra certainement tre compile, mais son comportement sera aberrant. Dans ce cas, revenez dans Interface Builder et vriez les connexions. Il est trs facile de passer ct de certains avertissements lors de la premire compilation dun chier. Puisque Xcode effectue des compilations incrmentales, vous ne les verrez peut-tre plus, jusqu ce que vous nettoyiez et recompiliez lintgralit du projet. Si vous tes bloqu, tentez un nettoyage et une recompilation.

Chapitre 1

Prsentation de Cocoa

Comment apprendre
Dans mes cours, jaccueille toutes sortes de personnes : brillantes et moins doues, motives et plus fainantes, exprimentes et novices. Invitablement, celles qui prote le mieux le cours prsentent la mme caractristique : elles restent concentres sur le sujet trait. Pour rester concentr, il faut commencer par dormir sufsamment. Je vous suggre dix heures de sommeil par nuit lorsque vous devez assimiler de nouvelles ides. Avant de repousser ce conseil, faites le test. Vous vous rveillerez frais et dispos pour apprendre. La cafine ne remplace pas le sommeil. Le deuxime point important est darrter de rchir sur soi-mme. Lorsquils travaillent sur de nouvelles choses, de nombreux tudiants ont tendance penser que "cest difcile pour moi, je dois vraiment tre stupide". Puisque la stupidit est une chose absolument terrible dans nos cultures, les tudiants passent des heures dvelopper des arguments justiant pourquoi ils sont intelligents malgr leurs difcults. Si vous empruntez cette voie, vous allez vous disperser. Jai eu un chef qui se nommait Rock. Rock tait titulaire dun diplme en astrophysique de Cal Tech et navait jamais eu un poste dans lequel il utilisait ses connaissances du ciel. Un jour, je lui ai demand sil regrettait ce diplme. "En ralit, mon diplme en astrophysique prouve que jai une grande valeur, a-t-il rpondu. Dans ce monde, certaines choses sont simplement difciles. Lorsque je me bats contre quelque chose, je pense parfois que cest difcile pour moi, je dois vraiment tre stupide ; puis je me souviens que je suis titulaire dun diplme en astrophysique de Cal Tech. Je ne dois donc pas tre si stupide." Avant de poursuivre, dites-vous que vous ntes pas stupide et que certaines choses sont difciles. Convaincu de cela et bien dispos, vous tes prt conqurir Cocoa.

2
Premiers pas
Au sommaire de ce chapitre
Dans Xcode Dans Interface Builder Retour dans Xcode Documentation

De nombreux ouvrages dbutent par des aspects philosophiques. Si nous commencions ainsi, du papier prcieux serait gaspill. la place, ce chapitre va vous guider tout au long de lcriture de votre premire application Cocoa. Une fois celle-ci termine, vous serez enthousiasm et confus, mais prt pour la philosophie. Le premier projet est une application qui fait ofce de gnrateur de nombres alatoires. Elle possde deux boutons : Initialiser le gnrateur avec lheure et Gnrer un nombre alatoire. Une zone de texte afche le nombre gnr. Pour ce simple exemple, nous devrons dterminer le bouton sur lequel lutilisateur clique et gnrer une sortie. Il est possible que les explications des manipulations demandes, ainsi que leurs raisons dtre, vous semblent un tantinet vagues, voire brutales. Ne vous inquitez pas. Tous les dtails arriveront point nomm. Pour le moment, nous allons simplement jouer un peu. La Figure 2.1 prsente lapplication termine.
Figure 2.1
Lapplication termine.

12

Cocoa

Dans Xcode
Si les outils du dveloppeur ont t installs, vous trouverez Xcode dans le rpertoire /Developer/Applications/. Faites glisser lapplication sur le Dock, car vous allez devoir la lancer trs souvent. Lancez Xcode. Nous lavons mentionn prcdemment, Xcode conserve toutes les ressources qui composent votre application. Elles sont places dans le rpertoire du projet. La premire tape du dveloppement dune nouvelle application consiste crer un nouveau rpertoire de projet, avec le squelette par dfaut dune application. Crer un nouveau projet Dans le menu File, choisissez New Project. Dans la fentre qui apparat (voir Figure 2.2), choisissez le type de projet que vous souhaitez crer : Cocoa Application. Vous noterez que bien dautres types de projets sont galement disponibles.
Figure 2.2
Choisir le type du projet.

Voici les principaux types de projets que nous utiliserons dans cet ouvrage :
m m

Application. Ce programme cre des fentres. Tool. Un programme qui ne possde pas dinterface utilisateur graphique. En gnral, un outil est un utilitaire en ligne de commande ou un dmon qui sexcute en arrire-plan.

Chapitre 2

Premiers pas

13

Bundle ou framework. Il sagit dun rpertoire de ressources qui peuvent tre utilises dans une application ou un outil. Un bundle, galement appel plug-in, est charg dynamiquement au moment de lexcution. En gnral, une application se lie un framework au moment de la compilation.

Pour le nom du projet, saisissez RandomApp (voir Figure 2.3). En gnral, chaque mot qui compose le nom dune application commence par une lettre majuscule. Vous pouvez galement indiquer o sera cr le rpertoire du projet. Par dfaut, il sagit de votre dossier de dpart. Cliquez sur le bouton Finish.
Figure 2.3
Nommer le projet.

Le rpertoire du projet, contenant le squelette dune application, est cr automatiquement. partir de ce squelette, nous allons crer le code source dune application complte, puis compiler ce code source en application oprationnelle. En examinant le nouveau projet dans Xcode, nous en obtenons une vue gnrale sur le ct gauche de la fentre. Chaque lment de cette vue reprsente un type dinformation utile au programmeur. Certains lments sont des chiers, dautres, des messages, comme les erreurs du compilateur ou les rsultats dune recherche. Nous allons modier certains chiers. Slectionnez RandomApp pour obtenir la liste des chiers qui seront compils an de crer une application. Le squelette du projet qui a t cr peut tre compil et excut. Lapplication rsultante est constitue dun menu et dune fentre. Pour compiler et excuter le projet, cliquez sur licne de la barre doutils qui reprsente un marteau et un cercle vert (voir Figure 2.4).

14

Cocoa

Figure 2.4
Squelette dun projet.

Cliquer sur Build and Go

Pendant que lapplication se lance, une icne sautille dans le Dock. Le nom de lapplication apparat dans le menu. Cela signie quelle est active. Sa fentre peut tre masque par celle dune autre application. Si vous ne la voyez pas, choisissez Hide Others dans le menu RandomApp. Vous devez obtenir une fentre vide (voir Figure 2.5).
Figure 2.5
Excuter le projet.

Chapitre 2

Premiers pas

15

Bien quelle ne fasse pas grand-chose, il sagit dj dune application parfaitement oprationnelle. Mme limpression fonctionne. Lapplication ne contient quune seule ligne de code. Examinons-la. Quittez RandomApp et revenez dans Xcode. La fonction main Slectionnez main.m en cliquant simplement dessus. Si vous double-cliquez sur le nom du chier, il souvrira dans une nouvelle fentre. En raison du grand nombre de chiers manipuls dans une journe, toutes ces fentres qui souvrent peuvent devenir rapidement pnibles. Il est parfois prfrable de travailler dans une seule fentre. Cliquez sur licne Editor an de dcomposer la fentre et de crer une vue ddition qui afche le code (voir Figure 2.6).
Figure 2.6
La fonction main().
Cliquer sur Editor pour afficher le code

En rgle gnrale, nous ne modions pas le chier main.m dans un projet de type Application. La fonction main() par dfaut appelle simplement NSApplicationMain(), qui, son tour, charge les objets de linterface utilisateur prsents dans un chier nib. Les chiers nib sont crs avec Interface Builder. (Info : NIB signie NeXT Interface Builder, et NS est le diminutif de NeXTSTEP.) Aprs avoir charg le chier nib, lapplication attend simplement les actions de lutilisateur. Lorsquil clique ou tape sur le clavier, notre code est appel automatiquement. Si vous navez jamais crit dapplication avec une interface utilisateur graphique, cela vous tonne peut-tre : lutilisateur a le contrle et votre code ragit simplement ses actions.

16

Cocoa

Dans Interface Builder


Dans la vue gnrale, sous Resources, nous voyons un chier nib nomm MainMenu.nib. Double-cliquez dessus pour louvrir dans Interface Builder (voir Figure 2.7). De nombreuses fentres vont apparatre, cest donc le bon moment de masquer les autres applications. Dans le menu Interface Builder, vous trouverez la commande Hide Others.
Figure 2.7
MainMenu.nib.
Menu de l'application Fentre de la bibliothque

Fentre de l'application

Fentre du fichier nib

Interface Builder permet de crer et de modier les objets de linterface utilisateur, comme les fentres et les boutons, et de les enregistrer dans un chier. Nous pouvons galement crer des instances de nos propres classes et tablir des connexions entre ces instances et les objets standard de linterface utilisateur. Lorsquun utilisateur interagit avec les objets de linterface, les connexions dnies entre ces objets et nos classes dclenchent lexcution de notre code. La fentre Library Cest dans la fentre Library que se trouvent les widgets que nous pouvons dposer dans notre interface utilisateur. Par exemple, si nous souhaitons un bouton, il suft de le faire glisser depuis la fentre Library.

Chapitre 2

Premiers pas

17

La fentre vierge La fentre vierge reprsente une instance de la classe NSWindow prsente dans notre chier nib. Lorsque nous dposons dans cette fentre des objets provenant de la bibliothque, ils sont ajouts au chier nib. Aprs avoir cr des instances de ces objets et modi leurs attributs, lenregistrement dun chier nib revient lyophiliser les objets dans le chier. Lors de lexcution de lapplication, le chier nib est lu et les objets sont ranims. Mais les informaticiens disent que "les objets sont archivs dans le chier nib par Interface Builder et sont dsarchivs au moment de lexcution de lapplication". Agencer linterface Nous allons la prsenter pas pas, mais noubliez pas que lobjectif est de crer une interface utilisateur semblable celle illustre la Figure 2.8.
Figure 2.8
Linterface termine.

Faites glisser un bouton depuis la fentre Library (voir Figure 2.9) vers la fentre vierge. Pour que cela soit plus facile, vous pouvez slectionner le groupe Cocoa > Views & Cells dans la partie suprieure de la fentre Library.
Figure 2.9
Faire glisser un bouton.

18

Cocoa

Double-cliquez sur le bouton pour xer son intitul Initialiser le gnrateur avec lheure. Copiez et collez le bouton. Fixez lintitul du nouveau bouton Gnrer un nombre alatoire. Faites glisser le champ de texte Label (voir Figure 2.10) et dposez-le sur la fentre.
Figure 2.10
Faire glisser un champ de texte.

Pour que le champ de texte soit aussi large que les boutons, faites glisser ses bords gauche et droit vers les cts de la fentre. Notez les lignes bleues qui apparaissent lorsque vous approchez du bord de la fentre. Ces guides vous aident respecter les rgles des interfaces graphiques utilisateur dApple.
Figure 2.11
Centrer le contenu du champ de texte.
Cliquer pour centrer le texte

Chapitre 2

Premiers pas

19

Diminuez la taille de la fentre. Pour que le contenu du champ de texte soit centr, vous devez utiliser linspecteur. Slectionnez le champ de texte et choisissez Attributes Inspector dans le menu Tools. Cliquez sur le bouton de centrage (voir Figure 2.11). Conseil de travail : le matin, jouvre la fentre de linspecteur et ne la ferme plus de la journe. La fentre du chier nib Dans le chier nib, certains objets, comme les boutons, sont visibles tandis que dautres, comme nos propres objets contrleurs, sont invisibles. Les icnes qui reprsentent les objets invisibles apparaissent dans la fentre du chier nib. Dans cette fentre, intitule MainMenu.nib, nous voyons les icnes qui reprsentent le menu principal et la fentre principale. First Responder est un objet ctif, mais dont le rle est essentiel. Nous y reviendrons au Chapitre 21. Dans ce chier nib, Files Owner correspond lobjet NSApplication de lapplication. Cet objet prend des vnements dans la le dattente des vnements et les dirige vers la fentre approprie. Nous dtaillerons Files Owner au Chapitre 12. Crer une classe En Objective-C, chaque classe est dnie par deux chiers : un chier den-tte et un chier dimplmentation. Le chier den-tte, galement appel chier dinterface, dclare les variables dinstance et les mthodes de la classe. Le chier dimplmentation dnit le rle de ces mthodes. Revenez dans Xcode et slectionnez File > New File pour crer une nouvelle classe Objective-C (choisissez Cocoa > Objective-C class). Nommez le chier Foo.m (voir Figure 2.12).
Figure 2.12
Crer une nouvelle classe.

20

Cocoa

Les chiers Foo.h et Foo.m sont ajouts notre projet. Sils napparaissent pas dans le groupe Classes, faites-les y glisser (voir Figure 2.13).
Figure 2.13
Placer Foo.h et Foo.m dans le groupe Classes.

Dans Foo.h, nous allons ajouter les variables dinstance et les mthodes de la classe. Les variables dinstance, qui sont des pointeurs vers dautres objets, sont appeles outlets. Les mthodes, dont linvocation peut tre dclenche par des objets de linterface utilisateur, sont appeles actions. Modiez Foo.h de la manire suivante :
#import <Cocoa/Cocoa.h> @interface Foo: NSObject { IBOutlet NSTextField *textField; } - (IBAction)seed:(id)sender; - (IBAction)generate:(id)sender; @end

partir de ce chier, un programmeur Objective-C dduira les trois points suivants : 1. Foo est une sous-classe de NSObject. 2. Foo possde une variable dinstance, textField, qui est un pointeur sur une instance de la classe NSTextField. 3. Foo possde deux mthodes, seed: et generate:, qui sont des mthodes daction.

Chapitre 2

Premiers pas

21

Par convention, les noms des mthodes et des variables dinstance commencent par une lettre minuscule. Si le nom est compos de plusieurs mots, chaque mot commence par une lettre majuscule, par exemple couleurFavorite. De mme, par convention, les noms de classes commencent par une majuscule, par exemple Foo. Enregistrez Foo.h. Crer une instance Nous allons prsent crer une instance de la classe Foo dans le chier nib. Retournez dans Interface Builder. partir de la fentre Library, faites glisser un Object bleu (sous Cocoa > Objects & Controllers) pour le dposer dans la fentre du chier nib (voir Figure 2.14).
Figure 2.14
Squelette dun projet.

Dans linspecteur Identity, xez sa classe Foo (voir Figure 2.15). Nos actions et nos outlets doivent apparatre dans linspecteur. Si ce nest pas le cas, vriez Foo.h. Vous avez d faire une erreur ou ne lavez pas enregistr.

22

Cocoa

Figure 2.15
Dnir la classe.

Identit

tablir les connexions La programmation oriente objet met laccent sur les rapports entre les objets. Nous allons prsenter les objets les uns aux autres. Les programmeurs Cocoa diraient : "Nous allons prsent dnir les outlets de nos objets." Pour prsenter un objet un autre, il suft de faire glisser, tout en maintenant enfonce la touche Contrle, le second (celui qui va connatre lautre) vers le premier (celui qui est prsent). La Figure 2.16 montre un diagramme qui illustre les connexions entre les objets de notre exemple.
Figure 2.16
Graphe dobjets.

Chapitre 2

Premiers pas

23

La variable dinstance textField de Foo doit pointer sur lobjet NSTextField de la fentre qui afche actuellement Label. Cliquez du bouton droit (ou faites un Contrle-clic si votre souris na quun bouton) sur le symbole qui reprsente linstance de Foo pour le champ de texte. Le panneau de linspecteur apparat. Faites glisser le cercle ct de textField vers le champ de texte qui afche Label (voir Figure 2.17).
Figure 2.17
Fixer loutlet textField.

Cette opration suft pour les pointeurs : vous devez simplement xer le pointeur textField de lobjet Foo de manire le faire pointer sur le champ de texte. Nous allons prsent xer loutlet target du bouton Initialiser pour quil pointe sur linstance de Foo. Par ailleurs, le bouton doit dclencher linvocation de la mthode seed: de Foo. Faites glisser, en maintenant la touche Contrle enfonce, le bouton Initialiser vers linstance de Foo. Lorsque le panneau apparat, choisissez seed: (voir Figure 2.18). Nous devons galement xer la variable dinstance target du bouton Gnrer pour quil pointe sur linstance de Foo et quil dclenche linvocation de la mthode generate:. Faites glisser, en maintenant la touche Contrle enfonce, le bouton Gnrer vers linstance de Foo. Choisissez generate: dans le panneau Received Actions (voir Figure 2.19).

24

Cocoa

Figure 2.18
Fixer la cible et laction du bouton Initialiser.

Figure 2.19
Fixer la cible et laction du bouton Gnrer.

Nous en avons termin avec Interface Builder. Enregistrez le chier, masquez lapplication, puis retournez dans Xcode.

Retour dans Xcode


Sil sagit de votre premier contact avec du code Objective-C, vous serez sans doute tonn de dcouvrir quil est assez diffrent dun code C++ ou Java. La syntaxe est peut-tre diffrente, mais les concepts sous-jacents sont identiques.

Chapitre 2

Premiers pas

25

Par exemple, voici comment dclarer une classe en Java :


import com.masociete.Tata; import com.masociete.Titi; public class Toto extends Tata implements Titi { ...mthodes et variables dinstance... }

Ce code indique que la classe Toto hrite de la classe Tata et implmente les mthodes dclares dans linterface Titi. Voici la dclaration de la classe quivalente en Objective-C :
#import <masociete/Tata.h> #import <masociete/Titi.h> @interface Toto: Tata <Titi> { ...variables dinstance... } ...mthodes... @end

Si vous connaissez Java, Objective-C nest en ralit pas si trange. Comme Java, Objective-C autorise seulement lhritage simple. Autrement dit, une classe ne possde quune seule classe mre (ou superclasse). Types et constantes en Objective-C Les programmeurs Objective-C utilisent quelques types qui nexistent pas dans le monde C.
m m m m m

id est un pointeur vers nimporte quel type dobjet. BOOL est identique char, mais il est utilis comme une valeur boolenne. YES vaut 1. NO vaut 0. IBOutlet est une macro dont lvaluation ne donne rien. Vous pouvez lignorer. Elle sert dindication Interface Builder lorsquil lit la dclaration dune classe dans un chier .h. IBAction quivaut void et sert dindication Interface Builder. nil est identique NULL. Nous utilisons nil la place de NULL pour les pointeurs vers des objets.

m m

26

Cocoa

Le chier den-tte Cliquez sur Foo.h. Examinez-le pendant un moment. Il dclare que Foo est une classe lle (ou sous-classe) de NSObject. Des variables dinstance sont dclares entre les accolades.
#import <Cocoa/Cocoa.h> @interface Foo: NSObject { IBOutlet NSTextField *textField; } - (IBAction)generate:(id)sender; - (IBAction)seed:(id)sender; @end

#import quivaut la directive #include du prprocesseur C. Cependant, #import sassure que le chier nest inclus quune seule fois. <Cocoa/Cocoa.h> est import car il contient la dclaration de NSObject, qui est la superclasse de Foo.

La dclaration de la classe commence par @interface. Le caractre @ nest pas utilis dans le langage C. Pour rduire les conits entre le code C et le code Objective-C, les mots cls dObjective-C commencent par @. Voici quelques autres mots cls reconnus dans Objective-C : @end, @implementation, @class, @selector, @protocol, @property et @synthesize. En gnral, il est plus facile de saisir du code lorsque lindentation en fonction de la syntaxe est active. Dans les prfrences de Xcode, ouvrez le panneau Indentation et cochez la case intitule Syntax-aware indenting (voir Figure 2.20).
Figure 2.20
Activer lindentation en fonction de la syntaxe.

Chapitre 2

Premiers pas

27

Modier le chier dimplmentation Examinons prsent Foo.m. Il contient limplmentation des mthodes. En C++ ou Java, voici comment une mthode serait implmente :
public void incrementer(Object source) { compteur++; champTexte.setIntValue(compteur); }

En clair, cela signie "incrementer est une mthode dinstance publique qui prend un objet en seul argument. La mthode ne retourne aucune valeur. Elle incrmente la variable dinstance compteur et envoie ensuite le message setIntValue() lobjet champTexte en lui passant compteur en argument". Voici la mthode quivalente en Objective-C :
- (void)incrementer:(id)source { compteur++; [champTexte setIntValue:compteur]; }

Objective-C est un langage trs simple. Les spcicateurs de visibilit nexistent pas : toutes les mthodes sont publiques et toutes les variables dinstance sont protges. En ralit, il existe des spcicateurs de visibilit pour les variables dinstance, mais ils sont rarement utiliss. Par dfaut, ces variables sont protges et cela fonctionne parfaitement. Au Chapitre 3, nous reviendrons en dtail sur Objective-C. Pour le moment, copiez simplement les mthodes suivantes :
#import "Foo.h" @implementation Foo - (IBAction)generate:(id)sender { // Gnrer un nombre entre 1 et 100. int generated; generated = (random() % 100) + 1; NSLog(@"Nombre gnr = %d", generated); // Demander au champ de texte de modifier son contenu. [textField setIntValue:generated]; } - (IBAction)seed:(id)sender { // Initialiser le gnrateur de nombres alatoires avec lheure. srandom(time(NULL)); [textField setStringValue:@"Gnrateur initialis"]; } @end

28

Cocoa

Puisque IBAction quivaut void, aucune de ces mthodes ne retourne une valeur. Objective-C est une extension du langage C et, ce titre, nous permet dappeler les fonctions, comme random() et srandom(), fournies par les bibliothques C et Unix standard. Avant de compiler et dexcuter des applications, nous allons modier les prfrences de Xcode. Tout dabord, il existe un journal, habituellement appel la console, o toutes les erreurs dexcution du code apparaissent. Ensuite, au cours dune journe, vous basculerez plusieurs centaines de fois entre le chier .h et le chier .m correspondant. Le raccourci clavier quivalant cette opration est Commande-Option-Flche vers le haut. Vous souhaitez que les deux chiers apparaissent dans la mme fentre (voir Figure 2.21).
Figure 2.21
Fichiers connexes dans la mme fentre, et effacement du journal.

Activer

Compiler et excuter Lapplication est prsent termine. Cliquez sur Build and Go. Si lapplication est dj en cours dexcution, une bote de dialogue vous demande si elle doit tre stoppe. Conrmez larrt. Si le code contient une erreur, le compilateur gnre un message qui signale un problme. Une indication derreur apparat dans le coin infrieur droit de la fentre. Cliquez sur le lien Failed pour ouvrir la fentre Build Results, qui dtaille les problmes. Cliquez sur les diffrentes erreurs pour slectionner la ligne de code en cause dans la partie infrieure de la fentre. Dans lexemple illustr la Figure 2.22, le programmeur a oubli un point-virgule. Lancez lapplication. Cliquez sur les boutons pour constater la gnration des nombres alatoires. Flicitations, vous venez de crer une application Cocoa oprationnelle !

Chapitre 2

Premiers pas

29

Figure 2.22
Compiler lapplication.

Avez-vous vu des messages de journalisation sur la console ? Lorsque les choses se passent mal, les classes Cocoa envoient des messages sur la console. Vous devez donc la surveiller pendant les tests de lapplication. Pour lafcher, vous trouverez une commande dans le menu Run, mais il est sans doute prfrable de la conserver en permanence. Dans les prfrences de Xcode, activez lafchage de la console ds le lancement dune application (voir Figure 2.23).
Figure 2.23
Afcher la console lors du dmarrage dune application.

Toujours afficher la console

30

Cocoa

La mthode awakeFromNib Lapplication prsente un lger dfaut. Lorsquelle dmarre, le mot Label apparat dans le champ de texte la place de toute autre information intressante. Corrigeons ce problme. Nous allons faire en sorte que le champ de texte afche lheure et la date au moment du lancement de lapplication. Le chier nib est une collection dobjets qui ont t archivs. Lorsque le programme est dmarr, les objets reprennent vie avant que lapplication ne prenne en charge les vnements de lutilisateur. Ce mcanisme est un peu inhabituel. La plupart des concepteurs dinterface graphique gnrent un code source qui agence les lments de linterface. Au lieu de cela, Interface Builder permet au dveloppeur de modier ltat des objets de linterface et de lenregistrer dans un chier. Aprs avoir t ressuscits, mais avant le traitement des vnements, tous les objets reoivent automatiquement le message awakeFromNib. Nous allons ajouter une mthode awakeFromNib qui initialisera la valeur du champ de texte. Ajoutez la mthode awakeFromNib dans le chier Foo.m. Pour le moment, saisissez simplement son code. Vous le comprendrez plus tard. En bref, il cre une instance de NSCalendarDate qui reprsente lheure actuelle. Ensuite, il demande au champ de texte de xer sa valeur daprs le nouvel objet reprsentant la date :
- (void)awakeFromNib { NSCalendarDate *now; now = [NSCalendarDate calendarDate]; [textField setObjectValue:now]; }

Lordre dapparition des mthodes dans le chier na pas dimportance. Assurez-vous simplement de les ajouter aprs @implementation et avant @end. Vous naurez jamais appeler awakeFromNib ; cette mthode est invoque automatiquement. Compilez et excutez nouveau votre application. Vous devriez voir apparatre la date et lheure (voir Figure 2.24).
Figure 2.24
Lapplication termine.

En Cocoa, de nombreuses choses, comme awakeFromNib, sont appeles automatiquement. La confusion que vous pourriez ressentir en lisant ce livre peut provenir de ces

Chapitre 2

Premiers pas

31

invocations automatiques. Il nest pas toujours vident de savoir si des mthodes doivent tre appeles explicitement ou si elles le sont automatiquement. Jessaierai de faire une distinction claire.

Documentation
Avant de clore ce chapitre, vous devez savoir trouver la documentation, car elle vous sera trs utile si vous bloquez lors dun exercice. Pour accder la documentation, le plus simple est de choisir Documentation dans le menu Help de Xcode (voir Figure 2.25).
Figure 2.25
La documentation.

Si tout en maintenant la touche Option enfonce vous double-cliquez sur le nom dune mthode, dune classe ou dune fonction, Xcode recherche automatiquement ce terme dans la documentation.

Travail ralis
Nous venons de raliser toutes les tapes de cration dune application Cocoa simple :
m m m m m

crer un nouveau projet ; agencer une interface ; crer des classes personnalises ; connecter linterface aux classes personnalises ; ajouter du code aux classes personnalises ;

32

Cocoa

m m

compiler ; tester.

Examinons brivement la chronologie dune application. Lorsque le processus dmarre, il excute la fonction NSApplicationMain, qui cre une instance de NSApplication. Lobjet dapplication lit le chier nib principal et extrait les objets quil contient. Tous ces objets reoivent le message awakeFromNib. Ensuite, lobjet dapplication vrie la prsence dvnements. Le droulement de ces oprations est illustr la Figure 2.26.
Figure 2.26
Chronologie dune application.
Lapplication dmarre Le fichier nib est charg

Pour chaque objet du fichier nib : La classe reoit alloc afin de crer une instance La nouvelle instance reoit init Les valeurs des variables d'instance sont fixes

Chaque objet du fichier nib reoit awakeFromNib

Le code dans lobjet personnalis est dclench Les vues sont actualises si ncessaire

La boucle principale de gestion des vnements dbute Un vnement est lu depuis la file dattente des vnements

Lapplication se termine

Le code de lvnement dans une vue (comme un bouton) est dclench

Lorsquil reoit un vnement de type clavier ou souris, le serveur de fentres place les donnes correspondantes dans la le dattente des vnements, destination de lapplication concerne (voir Figure 2.27). Lobjet dapplication lit les donnes dun vnement partir de sa le dattente et les transmet un objet dinterface utilisateur, comme un bouton. Linvocation de notre code est alors dclenche. Si le code modie les donnes dune vue, celle-ci est nouveau afche. Ensuite, lobjet dapplication vrie si un nouvel vnement est prsent dans sa le dattente. Ce processus de vrication de larrive dvnements et de raction leur prsence constitue la boucle principale de gestion des vnements.

Chapitre 2

Premiers pas

33

Figure 2.27
Le rle du serveur de fentres.

Lorsque lutilisateur clique sur Quitter dans le menu, NSApp reoit le message terminate:. Cela termine le processus et tous les objets sont dtruits. tes-vous perplexe, troubl ? Poursuivez votre lecture avec le chapitre suivant an dobtenir des rponses vos interrogations.

3
Objective-C
Au sommaire de ce chapitre
Crer et utiliser des instances Utiliser les classes existantes Crer ses propres classes Le dbogueur Travail ralis Pour les plus curieux : mcanisme des messages

Il tait une fois un homme, nomm Brad Cox, qui avait dcid que le monde devait passer un style de programmation plus modulaire. Le langage C tait rpandu et puissant. Smalltalk tait un langage orient objet, non typ et lgant. Brad Cox a introduit dans C les mcanismes de classes et denvoi de messages de Smalltalk. Le rsultat, Objective-C, est une extension trs simple du langage C. En ralit, Objective-C tait lorigine un simple prprocesseur C et une bibliothque. Objective-C nest pas un langage propritaire. Il sagit dun standard ouvert qui existe depuis des annes dans le compilateur GNU C (gcc) de la FSF (Free Software Foundation). Cocoa a t dvelopp en utilisant Objective-C, et la programmation Cocoa se fait gnralement en Objective-C. Pour enseigner le langage C et les concepts de base de lorient objet, il faudrait un livre complet. Au lieu dcrire ce livre, ce chapitre suppose que vous avez dj quelques notions de C et de la programmation oriente objet et prsente les bases dObjective-C. Si vous correspondez ce prol, vous constaterez que lapprentissage dObjective-C est facile. Dans le cas contraire, louvrage The Objective-C Language dApple sera une bonne introduction.

36

Cocoa

Crer et utiliser des instances


Au Chapitre 1, nous avons mentionn que les classes servaient crer des objets, que les objets avaient des mthodes et quil tait possible denvoyer des messages aux objets pour dclencher linvocation des mthodes. Dans cette section, vous allez apprendre crer un objet, lui envoyer des messages et le dtruire lorsquil est devenu inutile. Nous allons prendre en exemple la classe NSMutableArray. Pour crer une nouvelle instance de cette classe, il suft de lui envoyer le message alloc :
[NSMutableArray alloc];

Cette mthode retourne un pointeur sur lespace qui a t allou pour lobjet. Nous pouvons placer ce pointeur dans une variable :
NSMutableArray *toto; toto = [NSMutableArray alloc];

Il est important de ne pas oublier que, dans le langage Objective-C, toto est uniquement un pointeur. Dans ce cas, il pointe sur un objet. Avant dutiliser lobjet dsign par toto, nous devons nous assurer quil a t initialis. La mthode init se charge de cette opration. Nous crivons donc un code semblable au suivant :
NSMutableArray *toto; toto = [NSMutableArray alloc]; [toto init];

tudions la dernire ligne. Elle envoie le message init lobjet sur lequel pointe la variable toto. Nous disons donc "toto est le destinataire du message init". Vous noterez que lenvoi dun message implique un destinataire (lobjet dsign par toto) et un message (init), le tout plac entre des crochets. Il est galement possible denvoyer des messages des classes, comme lillustre lenvoi du message alloc la classe NSMutableArray. La mthode init retourne lobjet initialis. Cest pourquoi les envois de ce message sont toujours crits comme suit :
NSMutableArray *toto; toto = [[NSMutableArray alloc] init];

Lorsquun objet est devenu inutile, nous pouvons le dtruire. Le Chapitre 4 traite de ce sujet (et de tout ce qui concerne NSAutoreleasePool).

Chapitre 3

Objective-C

37

Certaines mthodes prennent un argument. Dans ce cas, le nom de la mthode, appel slecteur, se termine par des deux-points (:). Par exemple, pour ajouter des objets la n dun tableau, nous utilisons la mthode addObject: (en supposant que tata est un pointeur sur un autre objet) :
[toto addObject:tata];

Si la mthode attend plusieurs arguments, le slecteur est en plusieurs parties. Par exemple, pour ajouter un objet un indice prcis, nous crivons le code suivant :
[toto insertObject:tata atIndex:5];

Notez que insertObject:atIndex: est un seul slecteur, non deux. Il dclenche linvocation dune mthode avec deux arguments. Cela pourra sembler trange aux programmeurs C et Java, mais trs classique aux programmeurs Smalltalk. La syntaxe facilite galement la lecture du code. Par exemple, il est assez frquent de voir une mthode C++ semblable la suivante :
if (x.intersectionArc(35.0, 19.0, 23.0, 90.0, 120.0))

Il est beaucoup plus facile de comprendre la signication du code suivant :


If ([x intersectionArcAvecRayon:35.0 centreAX:19.0 Y:23.0 debutAngle:90.0 finAngle:120.0])

Si ce code vous semble bizarre pour le moment, attendez un peu. La plupart des programmeurs nissent trs rapidement par apprcier la syntaxe denvoi de messages dObjective-C. Vous tes dj arriv au stade o vous pouvez lire du code Objective-C simple. Il est donc temps dcrire un programme qui va crer une instance de NSMutableArray et la remplir avec dix instances de NSNumber.

Utiliser les classes existantes


Sil nest pas dj ouvert, lancez Xcode. Fermez tous les projets sur lesquels vous tiez en train de travailler. Dans le menu File, choisissez New Project. Dans la fentre qui souvre, slectionnez Foundation Tool (voir Figure 3.1). Un Foundation Tool (outil de base) ne possde pas dinterface graphique et sutilise gnralement sur la ligne de commande ou en arrire-plan comme un dmon. Contrairement un projet dapplication, nous modions toujours la fonction main dun Foundation Tool.

38

Cocoa

Figure 3.1
Choisir le type du projet.

Nommez le projet lottery (voir Figure 3.2). Contrairement aux noms des applications, les noms des outils sont gnralement en minuscules.
Figure 3.2
Nommer le projet.

Lorsque le nouveau projet apparat, slectionnez lottery.m dans Source, puis modiez ce chier de la manire suivante :
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) {

Chapitre 3

Objective-C

39

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSMutableArray *array; array = [[NSMutableArray alloc] init]; int i; for (i = 0; i < 10; i++) { NSNumber *newNumber = [[NSNumber alloc] initWithInt:(i * 3)]; [array addObject:newNumber]; } for ( i = 0; i < 10; i++) { NSNumber *numberToPrint = [array objectAtIndex:i]; NSLog(@"Le nombre dindice %d est %@", i, numberToPrint); } [pool drain]; return 0; }

Voici une explication dtaille de ce code :


#import <Foundation/Foundation.h>

Cette ligne inclut les en-ttes de toutes les classes qui se trouvent dans le framework Foundation. Puisque ces en-ttes sont dj compils, cette instruction nest pas aussi lourde que vous pourriez le penser.
int main (int argc, const char

*argv[])

La fonction main est dclare comme elle le serait dans nimporte quel programme C Unix.
NSAutoreleasePool

*pool

= [[NSAutoreleasePool alloc] init];

Ce code dclare une variable et la fait pointer sur une nouvelle instance de NSAutoreleasePool. Nous reviendrons sur limportance des pools libration automatique (autorelease pool) au chapitre suivant.
NSMutableArray

*array;

Cette ligne dclare une variable : array est un pointeur sur une instance de NSMutableArray. Notez que le tableau nexiste pas encore. Le code dclare simplement un pointeur qui fera rfrence au tableau une fois quil aura t cr.
array = [[NSMutableArray alloc] init];

Voil la cration de linstance NSMutableArray et son affectation la variable array qui pointe dessus.
for (i = 0; i < 10; i++) { NSNumber *newNumber = [[NSNumber alloc] initWithInt:(i *3)]; [array addObject:newNumber]; }

40

Cocoa

Dans la boucle for, une variable locale, nomme newNumber, est cre. Elle pointe sur une nouvelle instance de NSNumber. Ensuite, cet objet est ajout au tableau. Le tableau neffectue aucune copie des objets NSNumber. la place, il conserve simplement une liste de pointeurs sur les objets NSNumber. Les programmeurs Objective-C font trs peu de copies dobjets, car cette approche est rarement ncessaire.
for ( i = 0; i < 10; i++) { NSNumber *numberToPrint = [array objectAtIndex:i]; NSLog(@"Le nombre dindice %d est %@", i, numberToPrint); }

Ce code afche le contenu du tableau sur la console. NSLog est une fonction semblable la fonction printf() de C. Elle prend une chane de format et une liste de variables spares par des virgules, dont les valeurs remplaceront les paramtres dans la chane de format. Lors de lafchage, NSLog ajoute avant la chane le nom de lapplication et une estampille temporelle. Avec printf, par exemple, il faut utiliser %x pour afcher un entier sous forme hexadcimale. Avec NSLog, nous disposons de toutes les options de printf, avec en plus %@ pour afcher un objet. Lobjet reoit le message description et la chane retourne remplace le paramtre %@. Nous reviendrons sur la mthode description un peu plus loin. Le Tableau 3.1 recense toutes les options reconnues par NSLog.
Tableau 3.1 : Options reconnues dans les chanes de format dObjective-C

Symbole
%@ %d, %D, %i %u, %U %hi %hu %qi %qu %x, %X %o, %O %f, %e, %E, %g, %G %c

Afchage identiant long long non sign court court non sign long long long long non sign long non sign sous forme hexadcimale long non sign sous forme octale double char non sign sous forme de caractre ASCII

Chapitre 3

Objective-C

41

Tableau 3.1 : Options reconnues dans les chanes de format dObjective-C (suite)

Symbole
%C %s %S %p %%

Afchage unichar sous forme de caractre Unicode char * (une chane C de caractres ASCII termine par null) unichar * (une chane C de caractres Unicode termine par null) void * (une adresse afche sous forme hexadcimale en commenant par 0x) caractre %

INFO Si vous trouvez que le signe @ qui prcde les guillemets dans @"Le nombre dindice %d est %@" est un peu trange, noubliez pas quObjective-C nest que le langage C avec quelques extensions. En particulier, les chanes sont des instances de la classe NSString. En C, les chanes de caractres sont simplement des pointeurs sur un tampon de caractres qui se termine par le caractre null. Les chanes C et les instances de NSString peuvent tre utilises dans le mme chier. Pour diffrencier les chanes C constantes et les NSString constants, nous devons placer le signe @ avant les guillemets ouvrants dune constante NSString:
// Chane C. char *toto; // NSString. NSString *tata; toto = "Voici une chane C"; tata = @"Voici une NSString";

En programmation Cocoa, nous utilisons principalement NSString. Lorsquune chane de caractres doit tre fournie, les classes des frameworks attendent un NSString. Cependant, si nous disposons dj dun ensemble de fonctions C qui attendent des chanes C, nous utiliserons plus souvent char *. Voici comment convertir des chanes C en NSString :
const char *toto = "Bla bla"; NSString *tata; // Crer un NSString partir dune chane C. tata = [NSString stringWithUTF8String:toto]; // Crer une chane C partir dun NSString. toto = [tata UTF8String];

Puisquun NSString peut contenir des chanes Unicode, il faut traiter correctement les caractres sur plusieurs octets dans les chanes C. Cela peut se rvler assez difcile et long. Outre le problme li aux octets multiples, nous devons galement tenir compte du fait que certaines langues se lisent de droite gauche. Ds que cest possible, utilisez un NSString la place dune chane C.

42

Cocoa

Le code de main() se termine ainsi :


[pool drain]; return 0; }

Nous reviendrons sur les pools libration automatique au chapitre suivant. Dans la barre doutils, la liste droulante Active Build Configuration propose deux choix : Debug et Release. Pendant que nous travaillons sur lapplication, il est prfrable de choisir le mode Debug. Avant de la livrer, nous effectuerons une compilation en mode Release. Quelles sont les diffrences entre ces deux modes ? Une compilation Release est universelle et ne contient pas de symbole de dbogage. Elle prend deux fois plus de temps et ninclut aucune information qui permet au dbogueur de travailler. Cliquez sur Build and Go (voir Figure 3.3).
Figure 3.3
Une excution complte.

Si la console napparat pas, slectionnez Run > Console. Envoyer des messages nil Avec la plupart des langages orients objet, le programme se plantera si un message est envoy nil. Les applications crites dans ces langages multiplient les tests de nil avant lenvoi dun message. Par exemple, en Java, les instructions suivantes sont trs frquentes :
if (toto!= null) { toto.faitCeTravail(); }

Chapitre 3

Objective-C

43

En Objective-C, rien ninterdit denvoyer un message nil. Le message est simplement annul, ce qui limine les tests de contrle. Par exemple, le code suivant se compile et sexcute sans aucune erreur :
id toto; toto = nil; int tata = [toto compter];

Cette approche diffre de celle des autres langages, mais vous vous habituerez rapidement. Si vous vous demandez pourquoi une mthode nest jamais appele, il est fort probable que le pointeur utilis, que vous supposez non nil, soit en ralit nil. Dans lexemple prcdent, quelle est la valeur affecte tata ? Zro. Si tata tait un pointeur, il serait x nil (cest--dire zro pour les pointeurs). Pour tous les autres types, la valeur est moins prvisible. NSObject, NSArray, NSMutableArray et NSString Nous avons utilis les objets Cocoa standard : NSObject, NSMutableArray et NSString. Les noms des classes fournies avec Cocoa commencent par le prfixe NS ; celles que nous allons crer ne commencent pas par NS. Ces classes font partie du framework Foundation. La Figure 3.4 prsente un graphe dhritage pour ces classes.
Figure 3.4
Graphe dhritage.

Examinons les mthodes les plus utilises de ces classes. La liste complte se trouve dans la documentation en ligne accessible partir du menu Help de Xcode.

44

Cocoa

NSObject
NSObject constitue la racine de toute la hirarchie des classes Objective-C. Voici une description des mthodes de NSObject les plus utilises :
- (id)init

Initialise le destinataire aprs allocation de sa mmoire. Un message init est gnralement associ un message alloc dans la mme ligne de code :
LaClasse *nouvelObjet = [[LaClasse alloc] init]; - (NSString *)description

Retourne un NSString qui dcrit le destinataire. Dans le dbogueur, la commande dafchage dun objet ("po") invoque cette mthode. Une bonne mthode description facilite le dbogage. Par ailleurs, si nous utilisons %@ dans une chane de format, lobjet de substitution reoit le message description. La valeur retourne par cette mthode est place dans la chane consigne. Par exemple, dans la fonction principale, la ligne
NSLog(@"Le nombre dindice %d est %@", i, numberToPrint);

quivaut
NSLog(@"Le nombre dindice %d est %@", i, [numberToPrint description]); - (BOOL)isEqual:(id)anObject

Retourne YES si le destinataire et anObject sont gaux, NO dans le cas contraire. Elle sutilise de la manire suivante :
if ([monObjet isEqual:autreObjet]) { NSLog(@"Ils sont gaux."); }

Mais que signie rellement gal ? Dans NSObject, cette mthode retourne YES si et seulement si le destinataire et anObject reprsentent le mme objet ; autrement dit, si les deux sont des pointeurs vers le mme emplacement en mmoire. Bien videmment, gal na pas toujours cette signication. Par consquent, cette mthode est rednie par de nombreuses classes an dimplmenter une ide dgalit mieux adapte. Par exemple, NSString rednit cette mthode an de comparer les caractres du destinataire et de anObject. Sils contiennent les mmes caractres, dans le mme ordre, les deux chanes sont considres gales.

Chapitre 3

Objective-C

45

Par consquent, si x et y sont des NSString, il existe une diffrence importante entre lexpression :
x == y

et lexpression
[x isEqual:y]

La premire compare les deux pointeurs. La seconde compare les caractres dans les chanes. Cependant, si x et y sont des instances dune classe qui ne rednit pas la mthode isEqual: de NSObject, les deux expressions sont quivalentes. NSArray Un NSArray est une liste de pointeurs vers dautres objets. Il est index par des entiers. Ainsi, lorsque le tableau contient n objets, ces objets sont indexs par les entiers 0 n 1. Il est interdit de placer des nil dans un NSArray. Autrement dit, il ny a pas de "trous" dans un NSArray, ce qui risque de perturber les programmeurs habitus au type Object[] de Java. NSArray drive de NSObject. Un NSArray est cr avec tous les objets qui sy trouveront. Il est impossible dajouter ou de retirer des objets dans une instance de NSArray. Nous disons que NSArray est immuable. Sa sous-classe modiable, NSMutableArray, sera prsente plus loin. Dans certains cas, limmuabilit est intressante. Grce cette caractristique, tout un ensemble dobjets peuvent partager un NSArray sans sinquiter de sa modication ventuelle par lun des objets. NSString et NSNumber sont galement immuables. Au lieu de modier une chane ou un nombre, nous devons simplement en crer un autre avec la nouvelle valeur. Dans le cas de NSString, il existe galement la classe NSMutableString, dont les instances peuvent tre modies. Voici les mthodes de NSArray les plus utilises :
- (unsigned)count

Retourne le nombre dobjets contenus dans le tableau.


- (id)objectAtIndex:(unsigned)i

Retourne lobjet dindice i. Si i dsigne un emplacement hors du tableau, une erreur dexcution est gnre.
- (id)lastObject

Retourne lobjet qui a la plus grande valeur dindice. Si le tableau est vide, retourne nil.

46

Cocoa

- (BOOL)containsObject:(id)anObject

Retourne YES si anObject est prsent dans le tableau. Cette mthode dtermine si un objet est prsent dans le tableau en envoyant un message isEqual: chaque objet du tableau et en passant anObject en paramtre.
- (unsigned)indexOfObject:(id)anObject

Recherche anObject dans le destinataire et retourne le plus petit indice de la valeur du tableau qui est gale anObject. Des objets sont considrs comme gaux si la mthode isEqual: retourne YES. Si aucun des objets du tableau nest gal anObject, cette mthode retourne NSNotFound. NSMutableArray
NSMutableArray drive de la classe NSArray mais la complte en lui adjoignant la possibilit dajouter et de supprimer les objets. Pour crer un tableau modiable partir dun tableau immuable, nous utilisons la mthode mutableCopy de NSArray.

Voici les mthodes de NSMutableArray les plus utilises :


- (void)addObject:(id)anObject

Insre anObject la n du destinataire. Il est interdit dajouter nil un tableau.


- (void)addObjectsFromArray:(NSArray *)otherArray

Ajoute les objets contenus dans otherArray la n de la liste des objets du destinataire.
- (void)insertObject:(id)anObject atIndex:(unsigned)index

Insre anObject dans le destinataire lemplacement dsign par index. Si cet emplacement est dj occup, les objets index et aprs sont dcals dune position an de faire de la place. Puisque index ne peut pas tre suprieur au nombre dlments du tableau, une erreur est gnre si anObject est nil ou si index est suprieur la taille actuelle du tableau.
- (void)removeAllObjects

Vide le destinataire de tous ses lments.


- (void)removeObject:(id)anObject

Retire du tableau toutes les occurrences danObject. Les correspondances sont dtermines en fonction de la rponse danObject au message isEqual:.

Chapitre 3

Objective-C

47

- (void)removeObjectAtIndex:(unsigned)index

Supprime lobjet lemplacement dsign par index et dplace tous les lments aprs index dune position vers le bas an de combler le trou. Une erreur est gnre si index correspond un emplacement aprs la n du tableau. Nous lavons mentionn prcdemment, il est impossible dajouter nil dans un tableau. Cependant, nous souhaitons parfois placer dans un tableau un objet qui ne reprsente rien. La classe NSNull est prcisment l pour cela. Il existe une seule instance de NSNull. Si nous voulons prvoir un emplacement vide dans un tableau, nous utilisons NSNull de la manire suivante :
[monTableau addObject:[NSNull null]];

NSString Un NSString reprsente un tampon de caractres Unicode. En Cocoa, toutes les manipulations de chanes de caractres se font avec NSString. Pour des raisons pratiques, le langage Objective-C reconnat galement la construction @"" pour crer un objet de chane constant partir dune chane ASCII 7 bits :
NSString *temp = @"Voici une chane constante.";

NSString drive NSObject. Voici ses mthodes les plus utilises :


- (id)initWithFormat:(NSString *)format, ...

Fonctionne comme sprintf. format est une chane qui contient des spcications, comme %d. Les arguments supplmentaires remplacent ces spcications :
int x = 5; char *y = "abc"; id z = @"123"; NSString *aString = [[NSString alloc] initWithFormat: @"Lentier %d, la chane C %s et la NSString %@", x, y, z]; - (unsigned int)length

Retourne le nombre de caractres du destinataire.


- (NSString *)stringByAppendingString:(NSString *)aString

Retourne un objet de chane obtenue en ajoutant aString au destinataire. Par exemple, le code suivant gnre la chane "Erreur : lecture du chier impossible".
NSString *baliseErreur = @"Erreur: "; NSString *texteErreur = @"lecture du fichier impossible"; NSString *messageErreur; messageErreur = [baliseErreur stringByAppendingString: texteErreur];

48

Cocoa

"Hrite de", "Utilise" et "Connat" Les programmeurs Cocoa dbutants ont tendance crer des sous-classes de NSString et de NSMutableArray. Ne faites pas comme eux. Les bons programmeurs Objective-C ne le font jamais. la place, ils utilisent NSString et NSMutableArray comme lments dobjets plus grands. Cette technique est appele composition. Par exemple, une classe CompteBancaire pourrait tre une sous-classe de NSMutableArray. En effet, un compte bancaire nest-il pas simplement une suite de transactions ? Le novice empruntera cette voie. En revanche, lexpert crera une classe CompteBancaire qui drive de NSObject et qui contient une variable dinstance nomme transactions qui pointera vers un NSMutableArray. Il est important de bien comprendre la diffrence entre utilise et est une sous-classe de. Le dbutant dira "CompteBancaire hrite de NSMutableArray", tandis que lexpert dira "CompteBancaire utilise NSMutableArray". Parmi les idiomes classiques dObjective-C, utilise est beaucoup plus frquent que est une sous-classe de. Nous verrons quil est beaucoup plus facile dutiliser une classe que de crer une sousclasse. Lhritage implique un code plus long et exige une meilleure comprhension de la classe mre. En utilisant la composition la place de lhritage, les dveloppeurs Cocoa peuvent tirer prot de classes trs puissantes sans rellement comprendre leur fonctionnement. Dans le langage fortement typ, comme C++, lhritage est essentiel. Dans un langage non typ, comme Objective-C, lhritage est simplement une astuce qui permet au dveloppeur dconomiser de la saisie. Ce livre ne contient que deux graphes dhritage. Tous les autres sont des graphes dobjets qui montrent les objets connus dautres objets. Il sagit dune information beaucoup plus importante pour le programmeur Cocoa.

Crer ses propres classes


L o je vis, le gouvernement dtat a dcid que les personnes sans instruction avaient globalement trop dargent : nous pouvons jouer la loterie chaque semaine. Imaginons quun billet de loterie possde deux numros entre 1 et 100. Nous allons crire un programme qui gnre des billets de loterie pour les dix semaines suivantes. Chaque objet LotteryEntry possde une date et deux entiers alatoires (voir Figure 3.5). En plus dapprendre crer des classes, vous allez construire un outil qui va certainement vous rendre riche.

Chapitre 3

Objective-C

49

Figure 3.5
Le programme complet.

Crer la classe LotteryEntry Nous allons tout dabord crer des chiers pour la classe LotteryEntry. Dans le menu File, choisissez New file, et slectionnez le type Objective-C class. Nommez le chier LotteryEntry.m (voir Figure 3.6).
Figure 3.6
Nom du chier.

Cette opration cre galement le chier LotteryEntry.h. LotteryEntry.h Saisissez le code suivant dans LotteryEntry.h :
#import <Foundation/Foundation.h> @interface LotteryEntry: NSObject { NSCalendarDate *entryDate;

50

Cocoa

int firstNumber; int secondNumber; } - (void)prepareRandomNumbers; - (void)setEntryDate:(NSCalendarDate *)date; - (NSCalendarDate *)entryDate; - (int)firstNumber; - (int)secondNumber; @end

Nous avons cr un chier den-tte pour une nouvelle classe nomme LotteryEntry. Cette classe hrite de NSObject. Le chier den-tte dclare trois variables dinstance : entryDate, firstNumber et secondNumber :
m m

entryDate est un NSCalendarDate. firstNumber et secondNumber sont des entiers.

Nous avons galement dclar cinq mthodes dans la nouvelle classe :


m

prepareRandomNumbers xera les variables firstNumber et secondNumber des valeurs alatoires comprises entre 1 et 100. Cette mthode ne prend aucun argument et ne retourne aucune valeur. entryDate et setEntryDate: permettront dautres objets de lire et de xer la valeur de la variable entryDate. La mthode entryDate retournera la valeur de la variable entryDate. La mthode setEntryDate: permettra de xer la valeur de la variable entryDate. Les mthodes qui permettent de lire et de xer les valeurs des variables sont appeles accesseurs (ou mthodes daccs).

Des accesseurs sont galement dnis pour lire les variables firstNumber et secondNumber. Mais aucun accesseur ne permet de xer la valeur de ces variables, car elles le seront directement dans la mthode prepareRandomNumbers.

LotteryEntry.m Saisissez le code suivant dans LotteryEntry.m :


#import "LotteryEntry.h" @implementation LotteryEntry - (void)prepareRandomNumbers { firstNumber = random() % 100 + 1; secondNumber = random() % 100 + 1; } - (void)setEntryDate:(NSCalendarDate *)date { entryDate = date; }

Chapitre 3

Objective-C

51

- (NSCalendarDate *)entryDate { return entryDate; } - (int)firstNumber { return firstNumber; } - (int)secondNumber { return secondNumber; } @end

Voici chaque mthode explique :


m

prepareRandomNumbers utilise la fonction standard random pour gnrer un nombre pseudo-alatoire. Loprateur modulo (%) et laddition avec 1 permettent dobtenir un nombre dans lintervalle 1100. setEntryDate: xe le pointeur entryDate une nouvelle valeur. entryDate, firstNumber et secondNumber retournent les valeurs des variables.

m m

Modier lottery.m Revenons prsent au chier lottery.m. De nombreuses lignes sont restes les mmes, mais plusieurs ont chang. La modication la plus importante concerne lutilisation dobjets LotteryEntry la place dobjets NSNumber. Voici le nouveau code comment (la saisie des commentaires est facultative) :
#import <Foundation/Foundation.h> #import "LotteryEntry.h" int main (int argc, const char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Crer lobjet contenant la date. NSCalendarDate *now = [[NSCalendarDate alloc] init]; // Initialiser le gnrateur de nombres alatoires. srandom(time(NULL)); NSMutableArray *array; array = [[NSMutableArray alloc] init]; int i; for (i = 0; i < 10; i++){ // Crer un objet date/heure qui corresponde i semaines // partir daujourdhui.

52

Cocoa

NSCalendarDate *iWeeksFromNow; iWeeksFromNow = [now dateByAddingYears:0 months:0 days:(i * 7) hours:0 minutes:0 seconds:0]; // Crer une instance de LotteryEntry. LotteryEntry *newEntry = [[LotteryEntry alloc] init]; [newEntry prepareRandomNumbers]; [newEntry setEntryDate:iWeeksFromNow]; // Ajouter lobjet LotteryEntry au tableau. [array addObject:newEntry]; } for (LotteryEntry *entryToPrint in array) { // Afficher son contenu. NSLog(@"%@", entryToPrint); } [pool drain]; return 0; }

Faites attention la deuxime boucle. Elle se fonde sur un mcanisme Objective-C pour numrer les membres dune collection. Le programme cre un tableau dobjets LotteryEntry, comme lillustre la Figure 3.7.
Figure 3.7
Graphe dobjets.

Implmenter une mthode description Compilez et excutez lapplication. Vous devez obtenir un rsultat semblable celui prsent la Figure 3.8.

Chapitre 3

Objective-C

53

Figure 3.8
Excution complte.

Ce nest pas vraiment ce que nous attendions. Le programme est suppos donner les dates et les numros jouer ces dates-l, alors que nous nobtenons que des nombres en hexadcimal. En ralit, ces valeurs sont donnes par la mthode description par dfaut dnie dans NSObject. Nous allons donc faire en sorte que les objets LotteryEntry se prsentent de manire plus intressante. Ajoutez une mthode description dans LotteryEntry.m :
- (NSString *)description { NSString *result; result = [[NSString alloc] initWithFormat:@"%@ = %d et %d", [entryDate descriptionWithCalendarFormat:@"%d %b %Y"], firstNumber, secondNumber]; return result; }

Compilez et excutez lapplication. Vous devez prsent obtenir la date et les numros (voir Figure 3.9).
Figure 3.9
Excution avec une mthode description.

54

Cocoa

NSCalendarDate Avant daborder de nouvelles ides, examinons plus attentivement la classe NSCalendarDate. Les instances de cette classe contiennent une date et une heure, un fuseau horaire et une chane de format. NSCalendarDate hrite de NSDate. Les instances de NSCalendarDate sont globalement immuables : il est impossible de changer le jour ou lheure dune date aprs que linstance a t cre, mais il est possible de modier sa chane de format et son fuseau horaire. En raison de cette caractristique, de nombreux objets partagent souvent un seul objet de date. Il est rarement ncessaire de crer une copie dun objet NSCalendarDate. Voici les mthodes de NSCalendarDate les plus utilises :
+ (id)calendarDate

Cette mthode cre et retourne une date calendaire initialise la date et lheure courante, avec le format par dfaut correspondant aux paramtres rgionaux. Le fuseau horaire est celui x sur lordinateur. Il sagit dune mthode de classe. Dans le chier dinterface, le chier dimplmentation et la documentation, les mthodes de classe sont facilement reconnaissables car elles commencent par + la place de . Linvocation dune mthode de classe est dclenche par lenvoi dun message la classe la place dune instance. Par exemple, voici comment invoquer cette mthode :
NSCalendarDate *aujourdhui; aujourdhui = [NSCalendarDate calendarDate]; + (id)dateWithYear:(int)year month:(unsigned)month day:(unsigned)day hour:(unsigned)hour minute:(unsigned)minute second:(unsigned)second timeZone:(NSTimeZone *)unFuseauHoraire

Cette mthode de classe retourne un objet libration automatique. Plus prcisment, elle cre et retourne une date calendaire initialise laide des valeurs indiques. La valeur de lanne doit inclure le sicle (par exemple 2001 la place de 1). Les autres valeurs sont standard : 1 12 pour le mois, 1 31 pour le jour, 0 23 pour les heures et 0 59 pour les minutes et les secondes. Lextrait de code suivant cre une date xe au 3 aot 2000, 16 h 00, pour le fuseau horaire PST (Pacic Standard Time). timeZoneWithName: retourne un objet NSTimeZone qui reprsente le fuseau horaire dont le nom est pass en paramtre :

Chapitre 3

Objective-C

55

NSTimeZone *pacific = [NSTimeZone timeZoneWithName:@"PST"] NSCalendarDate *ete = [NSCalendarDate dateWithYear:2000 month:8 day:3 hour:16 minute:0 second:0 timeZone:pacific]; - (NSCalendarDate *)dateByAddingYears:(int)year months:(int)month days:(int)day hours:(int)hour minutes:(int)minute seconds:(int)second

Cette mthode retourne une date partir des dcalages donns pour lanne, le mois, le jour, lheure, les minutes et les secondes. Un dcalage positif reprsente le futur, tandis quun dcalage ngatif indique le pass. Nous avons utilis cette mthode dans lottery.m. Voici comment crer une date qui correspond six mois aprs ete :
NSCalendarDate *hiver = [ete dateByAddingYears:0 months:6 days:0 hours:0 minutes:0 seconds:0]; - (int)dayOfCommonEra

Cette mthode retourne le nombre de jours couls depuis lanne 1 aprs JsusChrist.
- (int)dayOfMonth

Cette mthode retourne un nombre qui indique le jour du mois (1 31) du destinataire.
- (int)dayOfWeek

Cette mthode retourne un nombre qui indique le jour de la semaine (0 6) du destinataire (0 reprsente le dimanche).
- (int)dayOfYear

Cette mthode retourne un nombre qui indique le jour de lanne (1 366) du destinataire.
- (int)hourOfDay

Cette mthode retourne lheure (0 23) du destinataire.

56

Cocoa

- (int)minuteOfHour

Cette mthode retourne la valeur des minutes (0 59) du destinataire.


- (int)monthOfYear

Cette mthode retourne un nombre qui indique le mois de lanne (1 12) du destinataire.
- (void)setCalendarFormat:(NSString *)format

Cette mthode xe le format par dfaut du calendrier du destinataire. Il sagit dune chane de caractres qui contient des indicateurs de conversion de date, recenss au Tableau 3.2.
Tableau 3.2 : Indicateurs reconnus dans la chane de format dun calendrier

Symbole Signication
%y %Y %b %B %m %a %A %w %d %e %j %H %I %p %M %S

Anne sans le sicle (0099) Anne avec le sicle ("1990") Nom abrg du mois ("Jan") Nom complet du mois ("Janvier") Mois sous forme de nombre dcimal (0112) Nom abrg du jour de la semaine ("Lun") Nom complet du jour de la semaine ("Lundi") Jour de la semaine sous forme de nombre dcimal (06), o 0 reprsente le dimanche Jour du mois sous forme de nombre dcimal (0131) Identique %d mais sans le 0 initial Jour de lanne sous forme de nombre dcimal (001366) Heure au format 24 heures sous forme de nombre dcimal (0023) Heure au format 12 heures sous forme de nombre dcimal (0112) Sufxe AM/PM pour les paramtres rgionaux Minutes sous forme de nombre dcimal (0059) Secondes sous forme de nombre dcimal (0059)

Chapitre 3

Objective-C

57

Tableau 3.2 : Indicateurs reconnus dans la chane de format dun calendrier (suite)

Symbole Signication
%F %x %X %c %Z %z %%

Millisecondes sous forme de nombre dcimal (000999) Date avec la reprsentation qui correspond aux paramtres rgionaux Heure avec la reprsentation qui correspond aux paramtres rgionaux Raccourci pour %X %x, le format local de la date et de lheure Nom du fuseau horaire Dcalage du fuseau horaire en heures et minutes par rapport GMT (HHMM) Caractre %

- (NSDate *)laterDate:(NSDate *)anotherDate

Cette mthode, hrite de NSDate, compare le destinataire anotherDate et retourne le plus tard des deux.
- (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate

Cette mthode retourne lintervalle en secondes qui spare le destinataire et anotherDate. Si le destinataire est antrieur anotherDate, la valeur retourne est ngative. NSTimeInterval est identique double. Mthodes dinitialisation La fonction main contient les lignes suivantes :
newEntry = [[LotteryEntry alloc] init]; [newEntry prepareRandomNumbers];

Elles crent une nouvelle instance, puis appellent immdiatement prepareRandomNumbers pour initialiser les variables firstNumber et secondNumber. Cette opration devrait tre effectue par la mthode dinitialisation, ou initialiseur. Nous allons donc rednir la mthode init de la classe LotteryEntry. Dans le chier LotteryEntry.m, transformez prepareRandomNumbers en mthode init :
- (id)init { [super init]; firstNumber = random() % 100 + 1; secondNumber = random() % 100 + 1; return self; }

58

Cocoa

La mthode init commence par appeler linitialiseur de la classe mre, puis initialise ses propres variables et retourne ensuite self, qui est un pointeur sur lobjet luimme lobjet qui est en train dexcuter cette mthode. Si vous tes un programmeur Java ou C++, self est lquivalent de this. prsent, supprimez la ligne suivante dans lottery.m :
[newEntry prepareRandomNumbers];

Dans LotteryEntry.h, retirez la dclaration suivante :


- (void)prepareRandomNumbers;

Compilez et excutez votre programme pour vrier son bon fonctionnement. Dans Cocoa, quelques initialiseurs retourneront nil si linitialisation na pas t possible. Puisque ce peut tre le cas de linitialiseur de la classe mre, le programmeur consciencieux crira le code suivant :
- (id)init { if (![super init]) return nil; firstNumber = random() % 100 + 1]; secondNumber = random() % 100 + 1]; return self; }

Cette version fonctionnera dans tous les cas et se rvle la forme la plus juste. Cependant, aucune des classes utilises dans cet ouvrage na besoin de ce contrle. Pour des raisons de simplicit, nous lomettrons donc. Mthodes dinitialisation avec arguments La partie de code correspondante dans lottery.m doit prsent contenir les deux lignes suivantes :
LotteryEntry *newEntry = [[LotteryEntry alloc] init]; [newEntry setEntryDate:iWeeksFromNow];

Il serait beaucoup plus propre de passer la date en argument linitialiseur. Modiez ces lignes de la manire suivante :
LotteryEntry *newEntry = [[LotteryEntry alloc] initWithEntryDate:iWeeksFromNow];

Chapitre 3

Objective-C

59

Dclarez ensuite la mthode dans LotteryEntry.h :


- (id)initWithEntryDate:(NSCalendarDate *)theDate;

Changez et renommez la mthode init dans LotteryEntry.m :


- (id)initWithEntryDate:(NSCalendarDate *)theDate { if (![super init]) return nil; entryDate = theDate; firstNumber = random() % 100 + 1; secondNumber = random() % 100 + 1; return self; }

Compilez et excutez le programme. Il doit toujours fonctionner correctement. Cependant, la classe LotteryEntry prsente un problme. Nous voulons lenvoyer par courrier lectronique notre copain Rex. Celui-ci envisage de lutiliser dans son programme, mais ne ralise pas que nous avons crit initWithEntryDate:. Il crit donc les lignes de code suivantes :
NSCalendarDate *aujourdhui = [NSCalendarDate calendarDate]; LotteryEntry *grosGain = [[LotteryEntry alloc] init]; [grosGain setEntryDate:aujourdhui];

Ce code ne gnre pas derreur. Il remonte simplement dans larborescence dhritage jusqu ce quil trouve la mthode init de NSObject. Mais, dans ce cas, les variables firstNumber et secondNumber ne sont pas initialises correctement elles sont toutes deux gales zro. Pour viter que Rex ne souffre de sa propre ignorance, il faut rednir init an quelle invoque linitialiseur avec une date par dfaut :
- (id)init { return [self initWithEntryDate:[NSCalendarDate calendarDate]]; }

Ajoutez cette mthode dans votre chier LotteryEntry.m. Notez que le vritable travail est toujours ralis par initWithEntryDate:. Puisquune classe peut avoir plusieurs initialiseurs, celui qui se charge du travail sappelle initialiseur dsign. Si une classe possde plusieurs initialiseurs, linitialiseur dsign prend gnralement le plus grand nombre darguments. Vous devez clairement indiquer lequel est linitialiseur dsign. Vous remarquerez que linitialiseur dsign de NSObject est init.

60

Cocoa

Conventions de cration des initialiseurs


Voici les rgles que les programmeurs Cocoa sobligent suivre lors de la cration des initialiseurs : Il est inutile de crer un initialiseur dans une classe si ceux de la classe mre sufsent. Si un initialiseur doit tre cr, il faut rednir linitialiseur dsign de la classe mre. Si plusieurs initialiseurs sont crs, un seul doit se charger du vritable travail linitialiseur dsign. Linitialiseur dsign dune classe appelle linitialiseur dsign de la classe mre.

Un jour viendra o vous serez oblig de crer une classe qui doit absolument prendre un argument. Rednissez linitialiseur dsign de la classe mre an quil lance une exception :
- (id)init { [self dealloc]; @throw [NSException exceptionWithName:@"BNRBadInitCall" reason:@"Initialisation avec le mauvais initialiseur:" userInfo:nil]; return nil; }

Le dbogueur
La FSF (Free Software Foundation) a dvelopp le compilateur (gcc) et le dbogueur (gdb) qui sont fournis avec les outils du dveloppeur dApple. Au l des annes, Apple a apport des amliorations importantes ces deux outils. Cette section explique comment placer des points darrt, invoquer le dbogueur et examiner les valeurs des variables. En examinant le code, vous avez sans doute remarqu une marge grise gauche de la fentre. Si vous cliquez dans cette marge, un point darrt est ajout sur la ligne correspondante. Dans le chier lottery.m, placez un point darrt sur la ligne suivante (voir Figure 3.10) :
[array addObject:newEntry];

Compilez et excutez le code. Xcode dmarre le programme dans le dbogueur car nous avons dni des points darrt. Le dbogueur demande quelques secondes pour se lancer, puis il excute le programme jusquau point darrt (voir Figure 3.11).

Chapitre 3

Objective-C

61

Figure 3.10
Dnir un point darrt.

Cliquer pour crer un point darrt

Figure 3.11
Lexcution stoppe au point darrt.

Cliquer pour afficher la console gdb

La liste situe gauche afche les blocs dactivation de la pile. Puisque le point darrt se trouve dans main(), la pile nest pas trs profonde. Dans la vue gnrale de droite, nous pouvons voir les variables et leur valeur. La variable i vaut actuellement 0. Au-dessus des informations de la pile se trouvent les boutons qui permettent de suspendre, de poursuivre, dexcuter lintgralit dune fonction, dentrer dans lexcution dune fonction et dexcuter jusqu la n dune fonction. Cliquez sur le bouton Continue pour excuter une autre itration de la boucle. Cliquez sur le bouton Step Over pour excuter le code ligne par ligne.

62

Cocoa

tant issu du monde Unix, le dbogueur gdb est conu pour une utilisation depuis une console. Pour accder gdb en mode terminal, il suft de cliquer sur licne intitule Console. La console donne accs toutes les possibilits de gdb. Par exemple, la commande "print-object" ("po") est trs utile. Si une variable est un pointeur sur un objet, la commande "po" lui envoie le message description et le rsultat est afch sur la console. Essayez dafcher la variable newEntry :
po newEntry

Vous devez obtenir le rsultat fourni par votre mthode description (voir Figure 3.12).
Figure 3.12
Utiliser la console gdb.

Lorsque quelque chose se passe mal, des exceptions sont lances. Pour que le dbogueur sarrte ds larrive dune exception, il faut ajouter un point darrt symbolique. Tout point darrt qui ne correspond pas un numro de ligne est un point darrt symbolique. Dans notre exemple, nous avons besoin dun point darrt sur la fonction objc_exception_throw. Pour afcher la fentre des points darrt dans Xcode, choisissez loption de menu Run > Show > Breakpoints. Dsactivez (dcochez) le point darrt existant. Double-cliquez l o cest indiqu et saisissez objc_exception_throw (voir Figure 3.13). Pour tester ce point darrt automatique, essayons de consulter la valeur enregistre dans un indice qui se trouve en dehors dun tableau. Juste aprs la cration du tableau, demandez son premier objet :
array = [[NSMutableArray alloc] init]; NSLog(@"premier lment = %@", [array objectAtIndex:0]);

Compilez et dmarrez le programme. Il doit sarrter ds que lexception est lance.

Chapitre 3

Objective-C

63

Figure 3.13
Dnir un point darrt symbolique.

Lors du dbogage des programmes Cocoa, vous risquez de rencontrer un problme frquent : ils sexcutent lentement, pendant un moment assez long, dans un pitre tat. En utilisant un macro NSAssert(), nous pouvons faire en sorte que le programme lance une exception ds que cela va mal. Par exemple, dans initWithEntryDate:, nous pourrions lancer une exception si largument vaut nil. Ajoutez un appel NSAssert() :
- (id)initWithEntryDate:(NSCalendarDate *)theDate { if (![super init]) return nil; NSAssert(theDate!= nil, @"Largument ne doit pas tre nil."); entryDate = theDate; firstNumber = random() % 100 + 1; secondNumber = random() % 100 + 1; return self; }

Compilez et excutez le programme. Puisque le code est correct, aucune exception nest lance. Modiez donc lassertion an quelle soit incorrecte :
NSAssert(theDate == nil, @"Argument must be non-nil");

Compilez et excutez lapplication. Un message, qui comprend le nom de la classe et dune mthode, est journalis ; une exception est lance. Lorsque NSAssert() est employe avec intelligence, la dcouverte des bogues est beaucoup plus rapide. Les assertions ne seront probablement pas utiles une fois lapplication termine. Dans la plupart des projets, il existe deux congurations de compilation : Debug et Release. En mode Debug, il est intressant de contrler toutes les assertions. En revanche, en mode Release, ce nest pas la peine. Cest pourquoi il est trs frquent de dsactiver le contrle des assertions dans la conguration Release. Pour cela, afchez les informations de compilation en double-cliquant sur la cible lottery. Allez dans longlet Build

64

Cocoa

et slectionnez la conguration Release. Sous GCC Preprocessing, ajoutez NS_BLOCK_ASSERTIONS Preprocessor Macros (voir Figure 3.14).
Figure 3.14
Dsactiver le contrle des assertions.

prsent, si vous compilez et excutez lapplication en mode Release, vous constaterez que les assertions ne sont pas vries. Avant daller plus loin, corrigez votre assertion : elle doit vrier que les dates ne sont pas nil.
NSAssert() fonctionne uniquement lintrieur des mthodes Objective-C. Si nous devons ajouter une insertion dans une fonction C, nous utilisons NSCAssert().

Vous en savez prsent assez pour commencer utiliser le dbogueur. Vous trouverez des informations plus dtailles sur le dbogueur de la FSF dans la documentation disponible sur le site www.gnu.org/.

Travail ralis
Nous avons crit un programme simple en Objective-C, avec une fonction main() qui cre plusieurs objets. Certains de ces objets sont des instances de LotteryEntry, une classe que nous avons cre. Le programme consigne des informations sur la console. ce stade, vous avez acquis les principales connaissances quant Objective-C. ObjectiveC nest pas un langage complexe. La suite de cet ouvrage sintressera aux frameworks qui composent Cocoa. Nos prochaines applications vont toutes ragir aux vnements ; il ne sagira pas doutils en ligne de commande.

Pour les plus curieux : mcanisme des messages


Nous lavons mentionn prcdemment, un objet peut tre compar une structure C. NSObject dclare une variable dinstance nomme isa. Puisque NSObject est la racine de larborescence dhritage des classes, chaque objet possde sa variable isa, qui est un pointeur sur la structure de classe qui a cr lobjet (voir Figure 3.15). La structure de classe comprend les noms et les types des variables dinstance de la classe, ainsi que

Chapitre 3

Objective-C

65

limplmentation des mthodes de la classe. Et galement un pointeur vers la structure de classe de sa classe mre.
Figure 3.15
Chaque objet possde un pointeur sur sa classe.

Les mthodes sont indexes par le slecteur, qui est du type SEL. Bien que SEL soit dni comme char *, il est plus facile de le considrer comme un entier (int). Chaque nom de mthode est associ un entier unique. Par exemple, le nom de mthode addObject: pourrait tre associ au nombre 12. Lorsque nous recherchons des mthodes, nous utilisons le slecteur, non la chane @"addObject:". Dans les structures de donnes Objective-C, un tableau associe les noms des mthodes leur slecteur. La Figure 3.16 montre un exemple.
Figure 3.16
Le tableau des slecteurs.

slecteurs

chanes de caractres

la compilation, le compilateur examine les slecteurs ds quil rencontre un envoi de messages. Alors, linstruction
[monObjet addObject:votreObjet];

devient (en supposant que le slecteur daddObject: soit 12)


objc_msgSend(monObject, 12, votreObjet);

66

Cocoa

objc_msgSend() examine le pointeur isa de monObjet pour obtenir sa structure de classe et recherche la mthode associe au nombre 12. Si elle ne trouve aucune mthode, elle suit le pointeur vers la classe mre. Si celle-ci na pas de mthode correspondant 12, la recherche continue vers le sommet de larborescence. Lorsque la racine est atteinte, sans que la mthode soit trouve, la fonction lance une exception.

Cette gestion des messages est clairement une solution trs dynamique. Les structures de classe peuvent tre modies lexcution. En particulier, avec la classe NSBundle, il est relativement facile dajouter des classes et des mthodes un programme pendant son excution. Cette technique trs puissante est utilise pour crer des applications qui peuvent tre tendues par dautres dveloppeurs.

Exercice
Changez la chane de format des objets de date dans la classe LotteryEntry.

4
Gestion de la mmoire
Au sommaire de ce chapitre
Activer et dsactiver le ramasse-miettes Programmer avec le ramasse-miettes Programmer avec les compteurs de rfrences

Supposons quil existe deux instances de la classe Personne, chacune avec un pointeur couleurFavorite sur un objet de couleur. Si deux personnes partagent les mmes gots de couleur, les pointeurs couleurFavorite dsigneront le mme objet de couleur. Avec le temps, leurs gots en matire de couleur peuvent changer. un moment donn, elles pourraient en venir dtester la couleur (voir Figure 4.1).

Figure 4.1
Le problme.

Nous ne souhaitons pas que cette couleur orpheline occupe de lespace dans la mmoire de notre programme. Nous voulons que la mmoire soit dsalloue an de pouvoir y placer de nouveaux objets, mais nous devons tre certains que la couleur nest pas retire alors que des objets y font encore rfrence.

68

Cocoa

Ce problme est plutt complexe. Apple propose deux solutions : 1. Lancienne solution se fonde sur des compteurs de rfrences (retain counts) : chaque objet possde un compteur de rfrences qui indique le nombre dobjets dtenant des pointeurs vers cet objet. Si deux personnes prfrent la mme couleur, le compteur de rfrences de cette couleur doit tre gal 2. Lorsquil atteint zro, lobjet est dsallou. 2. La nouvelle solution, introduite dans OS X 10.5, est un ramasse-miettes (garbage collector) qui a en charge lintgralit du graphe des objets et qui recherche les objets ne pouvant pas tre atteints partir des variables. Les objets inatteignables sont automatiquement dsallous. Quels sont les avantages et les inconvnients de ces deux solutions ? Les compteurs de rfrences sont assez lourds. Nous devons garder (retain) explicitement les objets que nous souhaitons manipuler et les librer explicitement lorsquils ne nous intressent plus. Ce mcanisme cre galement un problme dsagrable : un objet A garde un objet B, et B garde A. Cela cre un lot dobjets inatteignables qui ne disparatront jamais, car ils se gardent entre eux. Il sagit dun cycle de gardes (retain cycle). La Figure 4.2 illustre un cycle de gardes classique.
Figure 4.2
Un lot dobjets inatteignables.
NSMutableArray (retainCount = 1) Personne retainCount =1 enfant parent

Personne retainCount = 1

Pourquoi ne pas toujours utiliser le ramasse-miettes ? Tout simplement parce quil nest pas disponible sur les systmes Mac OS antrieurs la version 10.5 et que les applications ne fonctionneront donc pas. Par ailleurs, le ramasse-miettes demande du temps processeur pour analyser les objets, la recherche de ceux qui ne sont plus atteignables. Cela peut dgrader les performances. Dans une application qui ralise du traitement audio ou vido, le ramasse-miettes peut provoquer des hoquets dans le traitement pendant quil effectue son analyse.

Chapitre 4

Gestion de la mmoire

69

Activer et dsactiver le ramasse-miettes


Dans la vue globale Groups and Files de Xcode, il existe un groupe nomm Targets. Chaque projet possde au moins une cible (target). La cible reprsente un processus de compilation et rsulte gnralement en un produit. Par exemple, un projet pourrait avoir deux cibles : la premire construit une application, tandis que la seconde construit limportateur Spotlight pour le chier de donnes de cette application. Reprenez le projet de loterie. Double-cliquez sur la cible lottery an douvrir linspecteur. Examinez le panneau Build. Cest l que nous pouvons ajuster tous les paramtres qui contrlent la compilation du programme. Dans le champ de recherche, saisissez Garbage. De nombreuses lignes disparaissent et vous obtenez Objective-C Garbage Collection. Vous pouvez alors choisir dactiver ou non le ramasse-miettes. Si vous slectionnez Unsupported, le ramasse-miettes nest pas activ (voir Figure 4.3).
Figure 4.3
Activer/dsactiver le ramasse-miettes.

Les deux autres choix activent le ramasse-miettes. Vous devez recompiler lapplication pour que les modications prennent effet. La diffrence entre Supported et Required ne prsente un intrt que si vous crez un framework ou un plug-in qui sera utilis dans une autre application. Le code de cet ouvrage est en mode dual. Avec un tel code, les fuites de mmoire nexistent pas, que le ramasse-miettes soit activ ou non. Lorsque nous crivons une application, nous pouvons choisir si nous souhaitons utiliser le ramasse-miettes. Nous ne sommes pas obligs de toujours crire du code en mode dual. Dans le futur, vous pourrez dcider que le compteur de rfrences nest pas pour vous et dutiliser le ramasse-miettes dans tous les programmes. Personne ne vous en voudra. Cependant, pour le moment, vous devez connatre le mcanisme des compteurs de rfrences. Lorsque vous examinez du code ancien ou du code qui utilise des frameworks

70

Cocoa

de bas niveau, par exemple CoreFoundation, vous devez comprendre le fonctionnement de ce mcanisme. Si le mcanisme du compteur de rfrences vous perturbe rellement, lisez la section suivante sur le ramasse-miettes et sautez la suite du chapitre. Dans les chapitres suivants, ignorez simplement les appels retain, release ou autorelease. Ces mthodes ne font rien lorsque le ramasse-miettes est activ. De mme, ignorez toute implmentation de dealloc, car elle ne sera jamais invoque si le ramasse-miettes est activ.

Programmer avec le ramasse-miettes


Une application possde une instance de NSGarbageCollector si et seulement si elle utilise le ramasse-miettes. Ajoutez la ligne en gras vers la n de lottery.m :
[pool drain]; NSLog(@"GC = %@", [NSGarbageCollector defaultCollector]); return 0;

Compilez et excutez lapplication. Le ramasse-miettes est-il utilis ? Si nous utilisons le ramasse-miettes, il est important de ne pas conserver de rfrences aux objets dont nous navons plus besoin. Dans la fonction main de lottery, nous devons xer les pointeurs now et array nil lorsquils sont devenus inutiles. Ajoutez les lignes en gras :
} // now est devenu inutile. now = nil; for (LotteryEntry *entryToPrint in array) { NSLog(@"%@", entryToPrint); } // array est devenu inutile. array = nil; [pool drain]; NSLog(@"GC = %@", [NSGarbageCollector defaultCollector]); return 0; }

prsent, le ramasse-miettes sait quil peut dsallouer linstance de NSCalendarDate sur laquelle pointait now et linstance de NSMutableArray sur laquelle pointait array. En ralit, le programme se terminera probablement avant que le ramasse-miettes ait le temps de dsallouer ces objets. Le ramasse-miettes facilite la tche du programmeur. Nous avons vu tout ce quil est ncessaire de savoir sur ce sujet. Le reste de ce chapitre est consacr aux compteurs de rfrences.

Chapitre 4

Gestion de la mmoire

71

Programmer avec les compteurs de rfrences


Pour la suite de ce chapitre, supposons que lapplication doit tre compatible avec Mac OS X 10.4. Par consquent, nous devons oublier le ct pratique du ramasse-miettes et utiliser le mcanisme des compteurs de rfrences. Chaque objet possde un compteur de rfrences de type entier. Lorsquun objet est cr par la mthode alloc, son compteur de rfrences est x 1. Lorsque ce compteur arrive zro, lobjet est dsallou. Le compteur de rfrences est incrment en envoyant le message retain lobjet. Il est dcrment en envoyant le message release lobjet. Le compteur de rfrences dun objet doit reprsenter le nombre dobjets qui possdent des rfrences sur cet objet. Lorsque le compteur de rfrences est gal zro, cela signie que plus personne na besoin de cet objet. Il est donc dsallou an que lespace mmoire quil occupait puisse servir nouveau. Ce principe est souvent expliqu par la mtaphore du chien et des laisses. Toute personne qui souhaite que le chien reste dans les parages le garde en attachant une laisse son collier. Plusieurs personnes peuvent retenir le chien. Tant que le chien a au moins une laisse, il ne peut pas partir. Ds que ce nombre de personnes tombe 0, le chien est libr. Le compteur de rfrences dun objet correspond donc au nombre de laisses sur cet objet (voir Figure 4.4).
Figure 4.4
Des objets qui se gardent.

Le compteur de rfrences apporte un contrle prcis sur la dsallocation des objets, mais il impose une gestion soigne des gardes et des librations des objets. Si un objet est libr trop tt, il sera dsallou prmaturment et le programme se crashera. Si un objet est gard trop tard, il ne sera jamais dsallou et la mmoire sera gaspille. Nous allons prsent modier le code pour grer correctement les gardes et les librations. Revenez au projet de loterie, en ayant dsactiv le ramasse-miettes.

72

Cocoa

Nous devons librer la date et le tableau ds quils sont devenus inutiles :


} // now est devenu inutile. [now release]; now = nil; for (LotteryEntry *entryToPrint in array) { NSLog(@"%@", entryToPrint); } // array est devenu inutile. [array release]; array = nil; [pool drain]; NSLog(@"GC = %@", [NSGarbageCollector defaultCollector]); return 0; }

Le tableau sera dornavant correctement dsallou avant que le processus ne se termine. Lorsquun objet est ajout un tableau, aucune copie de lobjet nest cre. la place, le tableau stocke un pointeur sur lobjet et lui envoie le message retain. Lorsque le tableau est dsallou, tous les objets quil contient reoivent le message release. Lorsquun objet est retir dun tableau, il reoit galement le message release. Quen est-il des objets LotteryEntry ? Examinons rapidement la vie dun objet LotteryEntry dans notre application :
m m m

Lorsquun tel objet est cr, son compteur de rfrences est x 1. Lorsquil est ajout au tableau, son compteur de rfrences passe 2. Lorsque le tableau est dsallou, lobjet est libr. Cette opration dcrmente le compteur de rfrences, qui vaut donc 1.

Lobjet LotteryEntry nest pas dsallou. Dans cet exemple, le processus se termine juste aprs et le systme dexploitation rcupre toute la mmoire. Par consquent, labsence de dsallocation ne constitue pas vraiment un problme. Cependant, si un programme sexcute pendant une longue dure, une telle fuite de mmoire pourrait se rvler problmatique. Pour vous exercer tre un programmeur Objective-C consciencieux, corrigez le code. Aprs avoir insr le numro dans le tableau, librez-le. Voici la nouvelle version de la boucle :
LotteryEntry *newEntry; newEntry = [[LotteryEntry alloc] initWithEntryDate:iWeeksFromNow]; [array addObject:newEntry]; [newEntry release]; }

Chapitre 4

Gestion de la mmoire

73

Implmenter dealloc Lorsquun objet dont le compteur de rfrences vaut 1 reoit le message release, la mthode dealloc est invoque. Elle doit librer tous les objets qui sont retenus, puis appeler la mthode dealloc de la classe mre. Ajoutez la mthode dealloc suivante dans LotteryEntry.m :
- (void)dealloc { NSLog(@"dsallocation de %@", self); [entryDate release]; [super dealloc]; }

Dans la mthode initWithEntryDate:, il ne faut pas oublier de retenir entryDate. Ajoutez linvocation correspondante retain :
- (id)initWithEntryDate:(NSCalendarDate *)theDate { if (![super init]) return nil; entryDate = [theDate retain]; firstNumber = random() % 100 + 1; secondNumber = random() % 100 + 1; return self; }

Vriez que le ramasse-miettes est dsactiv, puis compilez et excutez lapplication. Elle doit fonctionner parfaitement et vous devriez constater la dsallocation des billets (voir Figure 4.5).
Figure 4.5
Excuter lapplication sans le ramasse-miettes.

74

Cocoa

Cependant, il reste encore une fuite de mmoire. Crer des objets libration automatique Nous avons crit la mthode de description suivante :
- (NSString *)description { NSString *result; result = [[NSString alloc] initWithFormat:@"%@ = %d et %d", [entryDate descriptionWithCalendarFormat:@"%d %B %Y"], firstNumber, secondNumber]; return result; }

Ce code fonctionne parfaitement, mais il conduit une fuite de mmoire ennuyeuse. Lopration alloc produit toujours un objet dont le compteur de rfrences est gal 1. Par consquent, le compteur de rfrences de la chane retourne vaut 1. Tout objet qui demande la chane va la garder. Le compteur de rfrences passera alors 2. Lorsque la chane ne lintressera plus, lobjet la librera. Le compteur de rfrences reviendra alors 1. La chane nest donc jamais dsalloue. Nous pouvons alors essayer la modication suivante :
- (NSString *)description { NSString *result; result = [[NSString alloc] initWithFormat:@"%@ = %d et %d", [entryDate descriptionWithCalendarFormat:@"%d %B %Y"], firstNumber, secondNumber]; [result release]; return result; }

Ce code ne fonctionne pas du tout. Lorsque la chane reoit le message release, son compteur de rfrences passe zro et elle est donc dsalloue. Lobjet qui demande la chane reoit un pointeur sur un objet qui a t libr. Le problme est donc le suivant : nous devons retourner une chane, mais nous ne devons pas la garder. Ce problme est rcurrent dans les frameworks. Nous en arrivons donc lutilisation de NSAutoreleasePool dans les applications sans ramassemiettes. Des objets sont ajouts au pool de libration automatique (autorelease pool) courant lorsquils reoivent le message autorelease. Lorsque ce pool est vid, il envoie le message release tous les objets quil contient.

Chapitre 4

Gestion de la mmoire

75

Autrement dit, un objet libration automatique est marqu de manire recevoir un message release un moment donn dans le futur. Dans une application Cocoa, un pool de libration automatique est cr avant chaque traitement dun vnement ; il est vid aprs ce traitement. Ainsi, moins que les objets des pools de libration automatique ne soient retenus, ils seront dtruits ds que lvnement aura t trait. Pour notre exemple, voici la solution correcte :
- (NSString *)description { NSString *result; result = [[NSString alloc] initWithFormat:@"%@ = %d et %d", [entryDate descriptionWithCalendarFormat:@"%d %B %Y"], firstNumber, secondNumber]; [result autorelease]; return result; }

Rgles de libration Les objets crs avec alloc, new, copy ou mutableCopy ont un compteur de rfrences gal 1 et ne sont pas placs dans le pool de libration automatique. Si un objet est obtenu par nimporte quelle autre mthode, il faut supposer que son compteur de rfrences est gal 1 et quil se trouve dans le pool de libration automatique. Sil ne doit pas tre dsallou avec le pool de libration automatique, il doit tre gard explicitement.

Puisque nous avons frquemment besoin dobjets que nous nallons pas conserver, de nombreuses classes possdent des mthodes de classe qui retournent des objets libration automatique. Par exemple, NSString offre la mthode stringWithFormat:. La solution correcte la plus simple serait donc la suivante :
- (NSString *)description { return [NSString stringWithFormat:@"%@ = %d et %d", [entryDate descriptionWithCalendarFormat:@"%d %B %Y"], firstNumber, secondNumber]; }

Objets temporaires Un objet libration automatique ne sera pas libr avant la n de la boucle de gestion des vnements. Ce fonctionnement en fait un candidat parfait lobtention dun rsultat intermdiaire. Par exemple, sil existe un tableau dobjets NSString, il

76

Cocoa

est possible de crer une chane partir de tous les lments mis en majuscules et concatns :
- (NSString *)concatenerToutEnMajuscules { int i; NSString *res = @""; NSString *maj; for (i=0; i < [monTableau count]; i++) { maj = [[monTableau objectAtIndex:i] uppercaseString]; res = [NSString stringWithFormat:@"%@%@", res, maj]; } return maj; }

Avec cette mthode, si le tableau contient treize chanes, vingt-six chanes libration automatique seront cres : treize par uppercaseString et treize par stringWithFormat:. La chane constante initiale est un cas particulier et ne compte pas. Lune des chanes rsultantes est retourne et peut tre garde par lobjet qui la demande. Les vingtcinq autres chanes sont dsalloues automatiquement la n de la boucle des vnements. Notez que, dans cet exemple, les performances seront certainement meilleures en concatnant la chane mise en majuscules un NSMutableString au lieu de crer une nouvelle chane et de lajouter au pool de libration automatique chaque tour de boucle. Mthodes accesseurs Un objet possde des variables dinstance. Les autres objets ne peuvent pas accder directement ces variables. Pour que dautres objets puissent lire et xer une variable dinstance, un objet fournit gnralement un couple de mthodes daccs. Par exemple, si une classe Rex possde une variable dinstance nomme fido, la classe fournira probablement au moins deux autres mthodes : fido et setFido:. La mthode fido permet dautres objets de lire la valeur de la variable fido. La mthode setFido: leur permet de xer sa valeur. Lorsque la variable nest pas un pointeur, les mthodes accesseurs sont plutt simples. Par exemple, si une classe possde une variable dinstance toto de type int, voici les accesseurs crer :
- (int)toto { return toto; } - (void)setToto:(int)x { toto = x; }

Chapitre 4

Gestion de la mmoire

77

Elle permet aux autres objets dobtenir et de xer la valeur de toto. La question se complique lorsque toto est un pointeur sur un objet. Dans la mthode "set", il faut vrier que la nouvelle valeur est garde et que lancienne est libre (voir Figure 4.6). Si lon suppose que toto est un pointeur sur une NSCalendarDate, il existe trois idiomes classiques pour les mthodes "set". Les trois fonctionnent correctement et vous rencontrerez probablement des programmeurs Cocoa expriments qui dfendront la supriorit de lun ou de lautre. Nous verrons les avantages et les inconvnients de chacun.
Figure 4.6
Avant et aprs setToto.

Le premier idiome est "garder puis librer" :


- (void)setToto:(NSCalendarDate *)x { [x retain]; [toto release]; toto = x; }

Dans ce cas, il est important de garder avant de librer. Supposons que lordre soit invers. Si x et toto sont des pointeurs sur le mme objet dont le compteur de rfrences vaut 1, la libration provoquera la dsallocation de lobjet avant quil soit gard.

78

Cocoa

Inconvnient : sils ont la mme valeur, cette mthode effectue des oprations de garde et de libration inutiles. Le deuxime idiome est "vrier avant de modier" :
- (void)setToto:(NSCalendarDate *)x { if (toto!= x) { [toto release]; toto = [x retain]; } }

Dans ce cas, la variable nest affecte que si elle est diffrente de la valeur passe en argument. Inconvnient : une instruction if supplmentaire est ncessaire. Le troisime idiome est "librer automatiquement lancienne valeur" :
- (void)setToto:(NSCalendarDate *)x { [toto autorelease]; toto = [x retain]; }

Dans ce cas, lancienne valeur est libre automatiquement. Inconvnient : une erreur dans les compteurs de rfrences provoquera un crash dans une boucle dvnements droule aprs lerreur. Ce comportement ne facilite pas la dcouverte du bogue. Dans les deux premiers idiomes, le dysfonctionnement apparatra plus proche de lerreur. Par ailleurs, autorelease implique un surcot qui risque de rduire les performances. Puisque vous connaissez les inconvnients, vous pouvez choisir par vous-mme lidiome suivre. Dans ce livre, nous utiliserons "garder puis librer". La mthode "get" pour un objet est identique celle pour une variable qui nest pas un pointeur :
- (NSCalendarDate *)toto { return toto; }

Les programmeurs Java auraient certainement nomm cette mthode getToto. Il ne le faut pas. Les programmeurs Objective-C lappellent toto. Dans les idiomes classiques dObjective-C, une mthode qui commence par get prend en argument une adresse o des donnes peuvent tre copies. Par exemple, si nous possdons un objet NSColor et si nous souhaitons obtenir ses composantes rouge, verte, bleue et alpha, nous appellerons getRed:green:blue:alpha: de la manire suivante :
float r, g, b, a; [maCouleurFavorite getRed:&r green:&g blue:&b alpha:&a];

Chapitre 4

Gestion de la mmoire

79

Pour les lecteurs qui auraient oubli leur C, & retourne ladresse laquelle la variable conserve ses donnes. Si nous utilisons des accesseurs pour lire les variables, la mthode description ressemblera la suivante :
- (NSString *)description { return [NSString stringWithFormat:@"%@ = %d et %d", [self entryDate], [self firstNumber], [self secondNumber]]; }

Les puristes de lorient objet prtendront quil sagit de la seule implmentation correcte de la mthode description. Modiez setEntryDate: dans LotteryEntry.m pour retenir correctement la nouvelle valeur et librer lancienne :
- (void)setEntryDate:(NSCalendarDate *)date { [date retain]; [entryDate release]; entryDate = date; }

Travail ralis
Nous avons crit un programme simple en Objective-C, avec une fonction main() qui cre plusieurs objets. Certains de ces objets sont des instances de LotteryEntry, une classe que nous avons cre. Le programme consigne des informations sur la console. ce stade, vous avez acquis les principales connaissances quant Objective-C. ObjectiveC nest pas un langage complexe. La suite de cet ouvrage sintressera aux frameworks qui composent Cocoa. Les prochaines applications ragiront toutes aux vnements ; il ne sagira pas doutils en ligne de commande.

5
Cible et action
Au sommaire de ce chapitre
Principales sous-classes de NSControl Dbuter lexemple SpeakLine Organiser le chier nib Implmenter la classe AppController Pour les plus curieux : xer la cible par programmation Conseils de dbogage

Il tait une fois une socit, nomme Taligent, qui avait t cre par IBM et Apple pour dvelopper un ensemble doutils et de bibliothques semblables Cocoa. Alors que Taligent tait au sommet de sa notorit, jai rencontr lun de ses ingnieurs un salon. Je lui ai demand de crer une application simple : une fentre contenant un bouton et o un clic sur le bouton ferait apparatre les mots "Hello, World!" dans un champ de texte. Lingnieur a cr un projet et sest mis crire des sous-classes tout-va : sousclasses de la fentre, du bouton et du gestionnaire dvnements. Ensuite, il a commenc gnrer du code : des dizaines de lignes pour placer le bouton et le champ de texte sur la fentre. Aprs 45 minutes, jai d partir. Lapplication ne fonctionnait toujours pas. Ce jour-l, jai compris que lentreprise navait aucun avenir. Deux annes plus tard, Taligent avait dnitivement ferm ses portes. La plupart des outils C++ et Java fonctionnent sur les mmes principes que ceux de Taligent. Le dveloppeur crit des sous-classes de nombreuses classes standard et gnre de nombreuses lignes de code pour que les contrles apparaissent sur les fentres. En ralit, la plupart de ces outils rencontrent un certain succs. Pour dvelopper une application fonde sur le framework AppKit, nous crerons rarement des sous-classes de celles qui reprsentent les fentres, les boutons ou les

82

Cocoa

vnements. la place, nous crirons des objets qui fonctionnent avec les classes existantes. De plus, nous ne produirons pas du code dafchage des contrles sur les fentres. la place, le chier nib contiendra toutes ces informations. Lapplication rsultante contiendra un nombre bien infrieur de lignes de code. Au dbut, cela vous semblera alarmant. Avec le temps, la plupart des programmes trouvent cela trs lgant. Pour comprendre le framework AppKit, il est sans doute plus facile de commencer par la classe NSControl. NSButton, NSSlider, NSTextView et NSColorWell sont toutes des sous-classes de NSControl. Un contrle possde une cible (target) et une action. La cible nest quun pointeur sur un autre objet. Laction est un message (un slecteur) envoyer la cible. Rappelez-vous le Chapitre 2. Nous avons dni la cible et laction de deux boutons : lobjet Foo est devenu la cible des deux boutons, tandis que seed: est devenue laction dun premier bouton (voir Figure 5.1) et generate:, laction du second.
Figure 5.1
Un bouton possde une cible (target) et une action.

Lorsque lutilisateur interagit avec le contrle, celui-ci envoie le message de son action sa cible. Par exemple, lors du clic sur le bouton, le bouton envoie laction la cible (voir Figure 5.2).
Figure 5.2
Le bouton envoie un message.

La mthode daction prend un argument : lmetteur. Le destinataire connat ainsi le contrle qui a envoy le message. Trs souvent, lmetteur sera rappel pour obtenir plus dinformations. Par exemple, une case cocher enverra son message daction lorsquelle est active et dsactive. Aprs avoir reu ce message, le destinataire peut rappeler la case cocher pour savoir si elle est actuellement active ou dsactive :
- (IBAction)basculerToto:(id)emetteur { BOOL estACtif = [emetteur state]; ... }

Chapitre 5

Cible et action

83

An de mieux comprendre NSControl, nous devons faire connaissance avec ses anctres. NSControl hrite de NSView, qui hrite de NSResponder, qui hrite de NSObject. Chaque membre de la famille apporte de nouvelles possibilits (voir Figure 5.3).
Figure 5.3
Graphe dhritage de NSControl.

Au sommet de la hirarchie de classe se trouve NSObject. Toutes les classes drivent de NSObject, qui fournit les mthodes de base : retain, release, dealloc et init. NSResponder est une sous-classe de NSObject. Les rpondeurs sont en mesure de traiter des vnements avec des mthodes comme mouseDown: et keyDown:. NSView est une sous-classe de NSResponder. Une vue NSView rserve un emplacement dans une fentre o elle se dessine elle-mme. Nous pouvons crer des sous-classes de NSView pour afcher des graphiques et permettre lutilisateur de glisser et de dposer des donnes. NSControl drive de NSView et ajoute la cible et laction.

84

Cocoa

Principales sous-classes de NSControl


Avant dutiliser des contrles, examinons brivement les trois contrles les plus utiliss : NSButton, NSSlider et NSTextField. NSButton Les instances de NSButton peuvent avoir diverses apparences : ovales, carres, cases cocher. Leurs comportements en rponse un clic peuvent galement tre diffrents : bascules (comme une case cocher) ou temporairement actives (comme la plupart des autres boutons). Aux boutons peuvent tre associs des icnes et des sons. La Figure 5.4 prsente linspecteur Attributes pour un NSButton dans Interface Builder.
Figure 5.4
Linspecteur dun bouton.

Voici les trois mthodes des boutons les plus souvent invoques :
- (void)setEnabled:(BOOL)yn

Lutilisateur peut cliquer sur un bouton activ. Les boutons dsactivs sont griss.
- (int)state

Cette mthode retourne NSOnState (qui vaut 1) si le bouton est slectionn, et NSOffState (qui vaut 0) si le bouton nest pas slectionn. Elle permet de savoir si une case est coche ou dcoche.
- (void)setState:(int)aState

Cette mthode slectionne ou dslectionne un bouton. Elle permet donc de cocher ou de dcocher une case directement depuis le code. aState doit tre x NSOnState pour cocher la case et NSOffState pour la dcocher.

Chapitre 5

Cible et action

85

NSSlider Les instances de NSSlider sont verticales ou horizontales. Elles peuvent envoyer laction la cible de manire continue pendant quelles sont manipules, ou attendre que lutilisateur relche le bouton de la souris. Un curseur peut tre complt de graduations et empcher les utilisateurs de choisir des valeurs situes entre ces graduations (voir Figure 5.5). Il existe galement des curseurs circulaires.
Figure 5.5
Linspecteur dun curseur.

Les curseurs en continu envoient le message d'action pendant que l'utilisateur dplace le curseur. Les curseurs non en continu n'envoient l'action qu'une fois le bouton de la souris relch.

Voici les deux mthodes de NSSlider les plus utilises :


- (void)setFloatValue:(float)x

Dplace le curseur la position x.


- (float)floatValue

Retourne la position actuelle du curseur.

NSTextField Une instance de NSTextField permet lutilisateur de saisir une seule ligne de texte. Les champs de texte peuvent tre modiables ou non. Les champs de texte non modiables servent gnralement dtiquettes dans une fentre. Par rapport aux boutons et aux curseurs, les champs de texte sont relativement complexes. Nous reviendrons en dtail sur ces contrles dans les chapitres suivants. La Figure 5.6 prsente linspecteur Attributes pour un NSTextField dans Interface Builder.

86

Cocoa

Figure 5.6
Linspecteur dun champ de texte.

Les champs de texte contiennent un emplacement pour une chane de caractres. Lorsque le champ est vide, lemplacement de la chane est afch en gris.
NSSecureTextField est une sous-classe de NSTextField. Il est utilis pour saisir discrtement des donnes, comme des mots de passe. Lorsque lutilisateur appuie sur les touches, des puces apparaissent la place des caractres saisis. Les fonctions couper et copier sont dsactives dans un NSSecureTextField.

Voici les mthodes de NSTextField les plus utilises :


- (NSString *)stringValue - (void)setStringValue:(NSString *)aString

Ces mthodes permettent dobtenir et de xer la chane de caractres afche dans le champ de texte.
- (NSObject *)objectValue - (void)setObjectValue:(NSObject *)obj

Ces mthodes permettent dobtenir et de xer les donnes afches dans le champ de texte sous forme dun type dobjet quelconque. Ce fonctionnement est trs utile lorsquon utilise un formateur. Les NSFormatter sont responsables de la conversion dune chane en un autre type, et inversement. Si aucun formateur nest dni, ces mthodes sappuient sur la mthode description. Par exemple, il est possible dutiliser un champ de texte pour laisser lutilisateur saisir une date. En tant que programmeur, nous avons besoin non pas de la chane que lutilisateur saisit, mais dune instance de NSCalendarDate. En attachant un NSDate-Formatter au champ de texte, nous faisons en sorte que la mthode objectValue de ce champ

Chapitre 5

Cible et action

87

de texte retourne un NSCalendarDate. Par ailleurs, lorsque nous appelons setObjectValue: avec un NSCalendarDate, le NSDateFormatter met cet objet sous forme dune chane de caractres prsente lutilisateur. Nous crerons une classe de formateur personnalis au Chapitre 23. La Figure 5.7 prsente dautres contrles dont vous pourriez avoir besoin. Dplacezles, tudiez-les et examinez leur fonctionnement lorsque vous excutez lapplication.
Figure 5.7
Divers contrles.

Dbuter lexemple SpeakLine


Pour servir dexemple lemploi des contrles, nous allons crire une application qui permet aux utilisateurs de saisir une ligne de texte et de lcouter, dicte par le synthtiseur vocal de Mac OS X. la n de ce chapitre, lapplication ressemblera celle illustre la Figure 5.8.
Figure 5.8
Lapplication termine.

La Figure 5.9 prsente le graphe des objets qui seront crs, ainsi que leurs relations les uns avec les autres. Toutes les classes dont les noms commencent par NS font partie des frameworks Cocoa et existent donc dj. Notre code sera dans la classe AppController.

88

Cocoa

Figure 5.9
Graphe dobjets.

NSButton action = sayIt: - setEnabled: target

NSTextField - stringValue textField AppController

NSButton action = stopIt: - setEnabled:

target

- sayIt: - stopIt: speechSynth NSSpeechSynthesizer - startSpeakingString: - stopSpeaking

Dans Xcode, crez un nouveau projet de type Cocoa SpeakLine.

Application. Nommez-le

Organiser le chier nib


Double-cliquez sur MainMenu.nib pour louvrir dans Interface Builder. partir de la fentre Library, faites glisser un champ de texte et deux boutons. Double-cliquez sur le champ de texte pour lui affecter le contenu "Les chaussettes de larchiduchesse sont sches" (lu avec un bel accent anglais), ou "Peter Piper picked a peck of pickled peppers", ou nimporte quel texte que vous aimeriez entendre prononc par la machine. Modiez les tiquettes des boutons an quils afchent Lire et Arrter. Le rsultat doit ressembler celui prsent la Figure 5.8. Dans Xcode, crez une nouvelle classe et nommez-la AppController. Elle sera la cible des deux boutons. Chaque contrle dclenchera une mthode daction diffrente. Ajoutez le code suivant dans AppController.h :
#import <Cocoa/Cocoa.h> @interface AppController: NSObject { IBOutlet NSTextField *textField; } - (IBAction)sayIt:(id)sender; - (IBAction)stopIt:(id)sender; @end

Pour crer une instance de AppController dans le chier nib, faites glisser un cube NSObject bleu dans la fentre du chier nib. Dans linspecteur Identity, xez sa classe AppController (voir Figure 5.10).

Chapitre 5

Cible et action

89

Figure 5.10
Fixer lidentit.

tablir des connexions dans Interface Builder Crer des connexions sapparente prsenter des gens. Nous disons "Mme Dupont, voici le Dr Martin". Sil est important que le Dr Martin connaisse galement Mme Dupont, nous poursuivons par "Dr Martin, voici Mme Dupont". Dans Interface Builder, nous faisons glisser lobjet qui doit connatre lautre objet vers celui-ci. Si ncessaire, nous pouvons galement procder dans lautre sens pour crer une connexion oppose. Par exemple, lorsquun utilisateur clique sur le bouton Arrter, celui-ci doit envoyer un message notre AppController. Par consquent, le bouton doit connatre AppController. Faites glisser, tout en maintenant la touche Contrle enfonce, le bouton vers AppController. Lorsque le menu contextuel apparat, slectionnez laction stopIt: (voir Figure 5.11).
Figure 5.11
Fixer laction du bouton Arrter.

90

Cocoa

Procdez de la mme manire pour affecter laction sayIt: dAppController au bouton Lire. Pour que le Mac puisse prononcer la ligne de texte, AppController doit la demander au champ de texte. Par consquent, AppController doit possder un pointeur vers le champ de texte. Faites un Contrle-clic sur AppController. Lorsque la liste des outlets apparat, faites glisser loutlet textField sur le champ de texte (voir Figure 5.12).
Figure 5.12
Connecter AppController au champ de texte.

ce stade, vous avez tabli toutes les connexions illustres dans le graphe dobjets de la Figure 5.9, sauf une. La connexion manquante, speechSynth, sera cre par le code, non dans Interface Builder. Outlet initialFirstResponder de NSWindow Lorsque lapplication sexcute et que la nouvelle fentre apparat, les utilisateurs ne devraient pas avoir cliquer sur le champ de texte avant deffectuer leur saisie. Nous pouvons indiquer la fentre la vue initiale qui doit recevoir les vnements du clavier. Faites un Contrle-clic sur licne de la fentre pour afcher son panneau des connexions. Faites glisser initialFirstResponder vers le champ de texte.

Chapitre 5

Cible et action

91

Implmenter la classe AppController


Nous devons prsent crire du code. Retournez dans Xcode et slectionnez le chier AppController.h. Ajoutez la variable dinstance speechSynth de type NSSpeechSynthesizer :
#import <Cocoa/Cocoa.h> @interface AppController: NSObject { IBOutlet NSTextField *textField; NSSpeechSynthesizer *speechSynth; } - (IBAction)sayIt:(id)sender; - (IBAction)stopIt:(id)sender; @end

Ouvrez le chier AppController.m. Cest l que nous allons programmer le travail des mthodes :
#import "AppController.h" @implementation AppController - (id)init { [super init]; // Les journaux peuvent aider le novice comprendre ce qui // se passe et trouver les bogues. NSLog(@"init"); // Crer une nouvelle instance de NSSpeechSynthesizer // avec la voix par dfaut. speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil]; return self; } - (IBAction)sayIt:(id)sender { NSString *string = [textField stringValue]; // La chane est-elle vide? if ([string length] == 0) { NSLog(@"la chane de %@ est vide", textField); return; } [speechSynth startSpeakingString:string]; NSLog(@"Lecture de: %@", string); } - (IBAction)stopIt:(id)sender { NSLog(@"Arrt en cours"); [speechSynth stopSpeaking]; } @end

92

Cocoa

Lapplication est termine. Compilez-la et excutez-la. Elle doit lire haute voix le texte saisi dans le champ de texte et vous devez pouvoir larrter au milieu de la phrase. Note nale : un lment de menu (une instance de NSMenuItem) possde galement une cible et une action. Tout le contenu de ce chapitre sapplique galement aux lments de menu.

Pour les plus curieux : xer la cible par programmation


Sachez que laction dun contrle est un slecteur. NSControl offre la mthode suivante :
- (void)setAction:(SEL)aSelector

Cependant, comment pouvons-nous obtenir un slecteur ? La directive @selector du compilateur dObjective-C lui demande de rechercher le slecteur notre place. Par exemple, voici comment xer laction dun bouton la mthode dessinerCasimir: :
SEL monSelecteur; monSelecteur = @selector(dessinerCasimir:); [monBouton setAction:monSelecteur];

la compilation, @selector(dessinerCasimir:) sera remplac par le slecteur de dessinerCasimir:. Pour dterminer le slecteur dun NSString lexcution, nous pouvons utiliser la fonction NSSelectorFromString() :
SEL monSelecteur; monSelecteur = NSSelectorFromString(@"dessinerCasimir:"); [monBouton setTarget:unObjetAvecUneMethodeDessinerCasimir]; [monBouton setAction:monSelecteur];

Exercice
Cet exercice reprsente un challenge important que vous devez russir avant de poursuivre. Sil est facile de suivre les instructions, vous devrez un jour crer votre propre application. Vous allez commencer prendre votre indpendance. Nhsitez pas revenir aux exemples prcdents en cas de besoin. Crez une application qui aura une seule fentre ouverte. Il ne sagit donc pas dune application base de documents. La Figure 5.13 prsente la fentre avant la saisie. La Figure 5.14 montre la fentre afche lutilisateur par lapplication.

Chapitre 5

Cible et action

93

Figure 5.13
Avant toute saisie.

Lorsque lutilisateur saisit une chane de caractres puis clique sur le bouton, le message afche la chane et le nombre de caractres quelle contient (voir Figure 5.14).
Figure 5.14
Aprs une saisie.

Il est important de savoir comment utiliser les classes Cocoa dans les applications. Pour cet exercice, vous devez trouver que la classe NSTextField offre les mthodes suivantes :
- (NSString *)stringValue - (void)setStringValue:(NSString *)aString

Il vous sera galement utile de connatre les mthodes suivantes de la classe NSString :
- (int)length + (NSString *)stringWithFormat:(NSString *),...

Vous devrez crer un objet contrleur avec deux outlets et une action. Cest certainement difcile, mais vous ntes pas stupide. Bonne chance !

Conseils de dbogage
Puisque vous crivez prsent du code sans le recopier simplement depuis cet ouvrage, vous tes prt recevoir quelques conseils de dbogage. Examinez toujours la console. Si un objet et Cocoa lancent une exception, elle sera consigne sur la console et la boucle des vnements sera redmarre. Si vous nexaminez pas la console, vous ne serez pas averti de lerreur. Utilisez toujours la conguration Debug pendant le dveloppement. Dans la conguration Release, les symboles de dbogage sont absents. Le dbogueur se comportera de

94

Cocoa

manire quelque peu trange lorsquil analysera un programme qui ne contient aucun symbole de dbogage. Voici quelques problmes classiques et leurs solutions :
m

Il ne se passe rien. Vous avez probablement oubli dtablir une connexion dans Interface Builder. Un pointeur est donc nil. Noubliez pas que les messages envoys nil nont aucun effet. Malgr la connexion, il ne se passe toujours rien. Vous avez probablement mal crit le nom dune mthode. Objective-C est sensible la casse. Par consquent, setToto: est totalement diffrent de settoto:. Essayez dajouter une instruction de journalisation ou de placer un point darrt sur la mthode pour savoir si elle est invoque. Lapplication se crashe. Lenvoi dun message un objet qui a t dsallou conduit au crash du programme. Ce comportement est plus difcile obtenir si vous utilisez le ramasse-miettes. Le dpistage de lorigine de ces dysfonctionnements peut se rvler complexe. En effet, lobjet incrimin a dj t dsallou. Une manire de procder consiste demander aux frameworks de convertir les objets en zombies au lieu de les dsallouer. Lorsque nous envoyons un message un zombie, il lance une exception explicative du genre "Vous avez essay denvoyer le message -count une instance libre de la classe Fido". Le dbogueur sarrtera alors sur la ligne correspondante. Pour activer les zombies, double-cliquez sur lexcutable dans Xcode. Dans le panneau Info, ajoutez deux variables denvironnement : NSZombiesEnabled YES, et CFZombieLevel 16 (voir Figure 5.15).

Les objets ne sont pas librs, mais le programme se crashe toujours. Vriez le type des arguments. Le code suivant, par exemple, est une bonne manire de crasher lapplication :
int x = 5; NSLog(@"x vaut %@", x);

Interface Builder ne me laisse pas crer une connexion. Un chier .h est corrompu. Manque-t-il un point-virgule ? Une variable est-elle dclare NSTabView la place de NSTableView ? Examinez attentivement le code.

Chapitre 5

Cible et action

95

Figure 5.15
Activer les zombies.

6
Objets assistants
Au sommaire de ce chapitre
Dlgation NSTableView et sa source de donnes Agencer linterface utilisateur tablir des connexions Modier AppController.m Pour les plus curieux : fonctionnement de la dlgation

Il tait une fois, avant Alerte Malibu, un homme sans nom. Knight Industries avait dcid que, si cet homme tait quip de pistolets, de roues et de fuses dappoint, il pourrait devenir larme parfaite contre le crime. Cette socit a tout dabord pens : "Crons une sous-classe et rednissons tout ce quil faut pour ajouter les pistolets, les roues et les fuses dappoint." Le problme tait que, pour crer une sous-classe de Michael Knight, il aurait fallu tout connatre de sa constitution interne pour y brancher des fusils et des fuses dappoint. Par consquent, elle a choisi de crer un objet assistant nomm KITT (Knight Industries 2000). Cette approche est totalement diffrente de celle choisie pour RoboCop. RoboCop tait un homme dont on a cr une sous-classe et que lon a tendu. Le projet RoboCop a ncessit des dizaines de chirurgiens pour que lhomme devienne une machine de guerre. Cest la solution quont choisie de nombreux frameworks orients objet. Tout en sapprochant du repaire dun traquant darmes, Michael Knight parlerait KITT laide de sa montre radio. "KITT, dirait-il, je dois aller de lautre ct du mur." KITT rpondrait en explosant le mur laide dune roquette. Aprs avoir dtruit le mur, KITT redonnerait le contrle Michael, qui avancerait tranquillement parmi les dcombres.

98

Cocoa

Dans le framework Cocoa, de nombreux objets sont tendus de cette manire. Autrement dit, il existe un objet qui doit tre tendu pour rpondre nos besoins. Au lieu de crer une sous-classe de la vue tableau, nous lui fournissons simplement un objet assistant. Par exemple, lorsquune vue tableau doit safcher, elle se tourne vers lobjet assistant et lui demande : "Combien de lignes de donnes dois-je afcher ?" Ou : "Que dois-je afcher dans la premire colonne, deuxime ligne ?" Ainsi, pour tendre une classe Cocoa existante, nous crivons le plus souvent un objet assistant. Ce chapitre se focalise sur la cration dobjets assistants et leur connexion aux objets Cocoa standard.

Dlgation
Dans lapplication SpeakLine du Chapitre 5, linterface serait plus facile utiliser si le bouton Arrter tait dsactiv jusqu ce que le synthtiseur vocal commence sa lecture et si le bouton Lire tait activ lorsque le synthtiseur vocal est silencieux. Pour cela, lapplication AppController doit activer le bouton lorsquelle dmarre le synthtiseur vocal et le dsactiver lorsquil sarrte. Dans le framework Cocoa, de nombreuses classes possdent une variable dinstance nomme delegate. Nous pouvons affecter cette variable un pointeur sur un objet assistant. Dans la documentation dune classe, les mthodes dlgues sont clairement dcrites. Par exemple, la classe NSSpeechSynthesizer dlgue les mthodes suivantes :
- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)finishedSpeaking - (void)speechSynthesizer:(NSSpeechSynthesizer *)sender willSpeakWord:(NSRange)characterRange ofString:(NSString *)string - (void)speechSynthesizer:(NSSpeechSynthesizer *)sender willSpeakPhoneme:(short)phonemeOpcode

Le programmeur dApple qui a crit la classe NSSpeechSynthesizer a dni ces points daccroche (hook). Il est Michael Knight. Nous sommes KITT. Parmi les trois messages que le synthtiseur vocal envoie son dlgu, seul le premier, speechSynthesizer:didFinishSpeaking:, nous intresse vraiment. Dans votre application, nous allons faire dAppController le dlgu du synthtiseur vocal et implmenter speechSynthesizer:didFinishSpeaking:. Cette mthode sera appele automatiquement lorsque lnonc sera termin. Le nouveau graphe dobjets est prsent la Figure 6.1.

Chapitre 6

Objets assistants

99

Figure 6.1
Nouveau graphe dobjets de SpeakLine.
NSButton action = sayIt: - setEnabled: target stopButton NSButton action = stopIt: - setEnabled:

NSTextField - stringValue

startButton

textField

AppController - sayIt: - stopIt: - speechSynthesizer:didFinishSpeaking:

target

speechSynth delegate NSSpeechSynthesizer - startSpeakingString - stopSpeaking

Nous ne sommes pas obligs dimplmenter les autres mthodes dlgues. Les mthodes implmentes seront invoques, les mthodes non implmentes seront ignores. Le premier argument est toujours lobjet qui envoie le message. Dans ce cas, il sagit du synthtiseur vocal. Dans AppController.m, affectez le synthtiseur vocal loutlet delegate :
- (id)init { [super init]; NSLog(@"init"); speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil]; [speechSynth setDelegate:self]; return self; }

Ensuite, ajoutez la mthode dlgue. Pour le moment, elle consigne simplement un message :
- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)complete { NSLog(@"fin = %d", complete); }

Compilez et excutez lapplication. La mthode dlgue est appele lors dun clic sur le bouton Arrter ou lorsque lnonc en cours est termin. complete vaut YES uniquement lorsque lnonc va jusqu la n.

100

Cocoa

Pour activer et dsactiver les boutons Arrter et Lire, nous avons besoin doutlets. Ajoutez les variables dinstance suivantes dans AppController.h :
IBOutlet NSButton *stopButton; IBOutlet NSButton *startButton;

Enregistrez le chier. Retournez dans Interface Builder et faites un Contrle-clic sur AppController. Faites glisser loutlet stopButton vers le bouton Arrter. Faites de mme pour loutlet startButton vers le bouton Lire (voir Figure 6.2).

Figure 6.2
Dnir les outlets stopButton et startButton.

Le bouton Arrter doit tre dsactiv lorsquil apparat initialement lcran. Slectionnez ce bouton et dsactivez-le dans linspecteur Attributes (voir Figure 6.3). Enregistrez le chier nib. Dans Xcode, nous allons modier le chier AppController.m pour que les boutons soient activs et dsactivs au bon moment. Dans sayIt:, nous activons le bouton Arrter et dsactivons le bouton Lire :
- (IBAction)sayIt:(id)sender { NSString *string = [textField stringValue]; if ([string length] == 0) { return; } [speechSynth startSpeakingString:string]; NSLog(@"Lecture de: %@", string);

Chapitre 6

Objets assistants

101

[stopButton setEnabled:YES]; [startButton setEnabled:NO]; }

Figure 6.3
Dsactiver le bouton Arrter.

Dcocher la case Enabled

Dans speechSynthesizer:didFinishSpeaking:, nous replaons les boutons dans leur tat initial :
- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)complete { NSLog(@"fin = %d", complete); [stopButton setEnabled:NO]; [startButton setEnabled:YES]; }

Compilez et excutez lapplication. Le bouton Arrter doit tre activ uniquement lorsque le synthtiseur est en cours de lecture. Le bouton Lire doit tre activ uniquement lorsque le synthtiseur est silencieux.

NSTableView et sa source de donnes


Nous allons prsent ajouter une vue tableau qui permettra lutilisateur de changer la voix (voir Figure 6.4).
Figure 6.4
Lapplication termine.

102

Cocoa

Une vue tableau permet dafcher des colonnes de donnes. Un NSTableView possde un objet assistant nomm dataSource (voir Figure 6.5). Le tableau suppose que la source de donnes offre certaines mthodes. Nous disons que "la source de donnes doit se conformer au protocole informel de NSTableDataSource". Plus simplement, elle doit implmenter les deux mthodes suivantes :
- (int)numberOfRowsInTableView:(NSTableView *)aTableView

La source de donnes retourne le nombre de lignes afcher.


- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex

La source de donnes retourne lobjet qui doit tre afch sur la ligne rowIndex de la colonne aTableColumn.
Figure 6.5
La source de donnes de NSTableView.

Si des cellules du tableau doivent pouvoir tre modies, il faut alors implmenter une autre mthode :
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex

La source de donnes prend lentre que lutilisateur place sur la ligne rowIndex de aTableColumn. Il est inutile dimplmenter cette mthode si le tableau nest pas modiable. Vous remarquerez que nous avons une attitude trs passive envers lapparition des donnes. La source de donnes attend jusqu ce que la vue tableau demande les donnes. Lors de leur premier contact avec NSTableView, ou NSBrowser et NSOutlineView, qui fonctionnent de manire trs similaire, la plupart des programmeurs veulent donner des ordres la vue tableau en lui disant "tu afcheras 7 sur la troisime ligne de la cinquime colonne". Cela ne fonctionne pas ainsi. Lorsquil est prt afcher la troisime ligne et la cinquime colonne, le tableau demande sa source de donnes lobjet correspondant. Notre classe est son serviteur. Pour que le tableau rcupre les informations mises jour, il faut invoquer sa mthode reloadData. La vue rechargera alors toutes les cellules visibles.

Chapitre 6

Objets assistants

103

Nous allons prsent faire en sorte que notre instance dAppController devienne la source de donnes de la vue tableau. Cela se fait en deux tapes : implmenter les deux mthodes indiques prcdemment et affecter linstance dAppController loutlet dataSource de la vue tableau. La Figure 6.6 prsente le graphe dobjets correspondant.
NSTextField - stringValue NSArray

NSButton action = sayIt: - setEnabled: target stopButton NSButton action = stopIt: - setEnabled: target

startButton

NSTableView textField voiceList tableView AppController dataSource - setEnabled: - selectRow:byExtendingSelection: - selectedRow

- sayIt: - stopIt: - speechSynthesizer:didFinishSpeaking:

delegate

speechSynth delegate NSSpeechSynthesizer - starSpeakingString - stopSpeaking

Figure 6.6
Graphe dobjets.

Tout dabord, ajoutez la dclaration de deux variables dinstance dans AppController.h :


#import <Cocoa/Cocoa.h> @interface AppController: NSObject { IBOutlet NSTextField *textField; NSSpeechSynthesizer *speechSynth; IBOutlet NSButton *startButton; IBOutlet NSButton *stopButton; IBOutlet NSTableView *tableView; NSArray *voiceList; }

Enregistrez le chier. Dans AppController.m, modiez la mthode init de manire initialiser voiceList :
- (id)init { [super init]; speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil]; [speechSynth setDelegate:self]; voiceList = [[NSSpeechSynthesizer availableVoices] retain]; return self; }

104

Cocoa

Agencer linterface utilisateur


Ouvrez MainMenu.nib. Nous allons modier la fentre pour quelle ressemble celle illustre la Figure 6.7.
Figure 6.7
Linterface termine.

Faites glisser un NSTableView sur la fentre (voir Figure 6.8).

Figure 6.8
Dposer une vue tableau sur la fentre.

Slectionnez la vue tableau an dexaminer ses attributs dans linspecteur (voir Figure 6.9). Ce sera peut-tre difcile car la vue tableau se trouve lintrieur dune vue dlement et la colonne de la vue tableau se trouve dans celle-ci. Faites des essais. Vous saurez que vous aurez slectionn la vue tableau lorsque lintitul de la fentre de linspecteur est Table View Attributes. Dans linspecteur, congurez le tableau pour quil ne contienne quune seule colonne. Dsactivez galement la slection de colonne. Cliquez sur len-tte de la colonne an de lintituler Voix.

Chapitre 6

Objets assistants

105

Figure 6.9
Inspecter la vue tableau.

tablir des connexions


Nous allons commencer par affecter notre instance dAppController loutlet dataSource de NSTableView. Pour cela, slectionnez le NSTableView. Faites un Contrleclic dans la vue tableau pour afcher le panneau des connexions. Faites glisser loutlet dataSource vers AppController. Si vous ne voyez pas dataSource dans linspecteur, vous avez en ralit slectionn le NSScrollView, non le NSTableView qui se trouve lintrieur. La vue dlement correspond lobjet qui prend en charge le dlement et les barres associes. Nous y reviendrons en dtail au Chapitre 17. Pour le moment, cliquez simplement lintrieur de la vue tableau jusqu ce que lintitul du panneau des connexions afche NSTableView (voir Figure 6.10).
Figure 6.10
Fixer loutlet dataSource de la vue tableau.

106

Cocoa

Par ailleurs, congurez la vue tableau pour que son dlgu soit AppController. Nous devons galement connecter loutlet tableView de lobjet AppController la vue tableau. Pour cela, faites un Contrle-clic sur AppController et connectez son outlet tableView la vue tableau (voir Figure 6.11).
Figure 6.11
Fixer loutlet tableView de lobjet AppController.

Enregistrez le chier nib et fermez-le.

Modier AppController.m
Ajoutez les mthodes de la source de donnes dans AppController.m :
- (int)numberOfRowsInTableView:(NSTableView *)tv { return [voiceList count]; } - (id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row { NSString *v = [voiceList objectAtIndex:row]; return v; }

Chapitre 6

Objets assistants

107

Lidentiant dune voix est une longue chane, comme com.apple.speech.synthesis.voice.Fred. Pour ne conserver que le nom Fred, il suft de remplacer la dernire mthode par la suivante :
- (id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row { NSString *v = [voiceList objectAtIndex:row]; NSDictionary *dict = [NSSpeechSynthesizer attributesForVoice:v]; return [dict objectForKey:NSVoiceName]; }

Les copies dcran prsentes dans ce chapitre ont t faites avec cette version. Construisez et excutez lapplication. Pour le moment, vous obtenez la liste des voix disponibles, mais le choix dune voix na aucun effet. Hormis loutlet dataSource, une vue tableau possde galement une outlet delegate. Le dlgu est inform des changements de slection. Dans AppController.m, implmentez la mthode tableViewSelectionDidChange:. La classe NSNotification sera prsente plus loin dans ce livre. Pour le moment, sachez simplement que nous passons un objet de notication en argument cette mthode dlgue.
- (void)tableViewSelectionDidChange:(NSNotification *)notification { int row = [tableView selectedRow]; if (row == 1) { return; } NSString *selectedVoice = [voiceList objectAtIndex:row]; [speechSynth setVoice:selectedVoice]; NSLog(@"nouvelle voix = %@", selectedVoice); }

Le synthtiseur vocal refuse de changer de voix pendant quil parle. Nous devons donc empcher lutilisateur de changer la ligne slectionne. La vue tableau doit tre active et dsactive en lien avec le bouton Lire :
- (IBAction)sayIt:(id)sender { NSString *string = [textField stringValue]; if ([string length] == 0) { return; } [speechSynth startSpeakingString:string]; NSLog(@"Lecture de: %@", string); [stopButton setEnabled:YES]; [startButton setEnabled:NO]; [tableView setEnabled:NO]; }

108

Cocoa

- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)complete { NSLog(@"fin = %d", complete); [stopButton setEnabled:NO]; [startButton setEnabled:YES]; [tableView setEnabled:YES]; }

Les utilisateurs voudront connatre la voix choisie par dfaut dans le tableau au dmarrage de lapplication. Dans awakeFromNib, nous slectionnons la ligne correspondante et procdons un dlement de la liste si ncessaire :
- (void)awakeFromNib { // Lorsque la vue tableau apparat lcran, la voix par dfaut // doit tre slectionne. NSString *defaultVoice = [NSSpeechSynthesizer defaultVoice]; int defaultRow = [voiceList indexOfObject:defaultVoice]; [tableView selectRowIndexes: [NSIndexSet indexSetWithIndex:defaultRow] byExtendingSelection:NO]; [tableView scrollRowToVisible:defaultRow]; }

Compilez et excutez lapplication. Pendant que le synthtiseur vocal dicte la phrase, un bip doit se faire entendre si lutilisateur tente de changer la voix. Lorsque le synthtiseur est silencieux, il doit pouvoir changer la voix. Erreurs classiques dans limplmentation dun dlgu Voici les deux erreurs les plus frquentes dans limplmentation dun dlgu :
m

Faute dorthographe dans le nom de la mthode. La mthode ne sera pas appele et vous ne recevrez aucune erreur ou aucun avertissement de la part du compilateur. La meilleure manire dviter ce problme consiste copier et coller la dclaration de la mthode partir de la documentation ou du chier den-tte. Oublier de xer loutlet delegate. Le compilateur ne gnrera aucun message derreur ou davertissement pour cette erreur.

Dlgus dobjets La dlgation est un motif de conception que vous rencontrerez en de nombreux points de Cocoa. Voici quelques classes du framework AppKit qui possdent une outlet delegate :
NSAlert NSAnimation NSApplication NSBrowser NSDatePicker NSDrawer NSFontManager NSImage NSLayoutManager

Chapitre 6

Objets assistants

109

NSMatrix NSMenu NSPathControl NSRuleEditor NSSavePanel NSSound NSSpeechRecognizer NSSpeechSynthesizer NSSplitView NSTabView NSTableView NSText NSTextField NSTextStorage NSTextView NSTokenField NSToolbar NSWindow

Pour les plus curieux : fonctionnement de la dlgation


Le dlgu nest pas oblig dimplmenter toutes les mthodes, mais celles qui sont implmentes seront invoques. Dans de nombreux langages, ce fonctionnement est impossible. Comment est-il obtenu en Objective-C ?
NSObject possde la mthode suivante :
- (BOOL)respondsToSelector:(SEL)aSelector

Puisque tous les objets hritent, directement ou indirectement, de NSObject, ils disposent tous de cette mthode. Elle retourne YES si lobjet dnit la mthode nomme aSelector. Vous remarquerez quaSelector est du type SEL, non NSString. Supposons que nous participions limplmentation de la classe NSTableView. Nous devons crire le code qui change la slection dune ligne pour une autre. Nous pensons donc "je dois consulter le dlgu". Pour cela, nous ajoutons un morceau de code semblable au suivant :
// La ligne slectionne va tre "rowIndex" // Comportement par dfaut. BOOL ok = YES; // Vrifier si le dlgu implmente la mthode. if ([delegate respondsToSelector:@selector(tableView:shouldSelectRow:)]) { // Excuter la mthode. ok = [delegate tableView:self shouldSelectRow:rowIndex]; } // Utiliser la valeur retourne. if (ok) { ...modifier la slection... }

110

Cocoa

Le message est envoy au dlgu uniquement sil implmente la mthode. Dans le cas contraire, le comportement par dfaut est appliqu. En ralit, le rsultat de respondsToSelector: est gnralement plac dans un cache gr par lobjet qui possde loutlet delegate. Cela permet damliorer considrablement les performances par rapport au code prcdent. Aprs avoir crit cette mthode, nous devons mentionner son existence dans la documentation de la classe. Si nous souhaitons avoir connaissance des contrles sur lexistence des mthodes du dlgu, nous pouvons rednir respondsToSelector: dans celui-ci :
- (BOOL)respondsToSelector:(SEL)aSelector { NSString *methodName = NSStringFromSelector(aSelector); NSLog(@"respondsToSelector:%@", methodName); return [super respondsToSelector:aSelector]; }

Vous pouvez ajouter cette mthode dans AppController.m.

Exercice : crer un dlgu


Crez une nouvelle application avec une fentre. crivez un objet qui sert de dlgu pour la fentre. Lorsque lutilisateur redimensionne la fentre, assurez-vous quelle est toujours deux fois plus large que haute. Voici la signature de la mthode dlgue implmenter :
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize

Le premier argument est la fentre en cours de redimensionnement. Le second est une structure C qui contient la taille demande par lutilisateur :
typedef struct _NSSize { float width; float height; } NSSize;

Voici comment crer un objet NSSize dont la largeur est gale 200 points et la hauteur, 100 points :
NSSize mySize; mySize.width = 200.0; mySize.height = 100.0; NSLog(@"mySize de largeur %f et de hauteur %f", mySize.width, mySize.height);

Vous pouvez xer la taille initiale de la fentre dans Interface Builder avec linspecteur Size.

Chapitre 6

Objets assistants

111

Exercice : crer une source de donnes


Vous avez cr une application qui gre un pense-bte. Lutilisateur saisira des tches dans un champ de texte. Lorsquil cliquera sur le bouton Ajouter, la chane sera ajoute un tableau modiable. La nouvelle tche apparatra galement la n de la liste (voir Figure 6.12).
Figure 6.12
Diagramme de lexercice.

"Tondre la pelouse" "Appeler la baby-sitter pour samedi soir" "Acheter 2 rouleaux de papier cadeau" "Commander un gteau"

Vous pouvez galement faire en sorte que le tableau soit modiable. Aide : NSMutableArray possde une mthode -replaceObjectAtIndex:withObject:.

7
Modles de conception KVC et KVO
Au sommaire de ce chapitre
KVC Liaisons KVO Crer des cls observables Proprits et leurs attributs Pour les plus curieux : chemin de cl Pour les plus curieux : KVO

Le modle de conception KVC (Key-Value Coding) dnit laccs la valeur dune variable daprs son nom. Ce nom nest rien dautre quune chane de caractres, mais il est appel cl (key). Par exemple, imaginons une classe nomme Personne avec une variable dinstance nomFamille de type NSString :
@interface Personne: NSObject { NSString *nomFamille; } ... @ends

Voici comment xer la valeur de la variable nomFamille dune instance de Personne :


Personne *p = [[Personne alloc] init]; [p setValue:@"Durand" forKey:@"nomFamille"];

114

Cocoa

La lecture de la valeur de nomFamille se fait de la manire suivante :


NSString *x = [s valueForKey:@"nomFamille"];

Les mthodes setValue:forKey: et valueForKey: sont dnies dans la classe NSObject. Mme si cela ne semble pas une rvolution, la possibilit de lire et de xer la valeur dune variable en indiquant son nom est rellement puissante. La suite de ce chapitre est un exemple simple qui illustrera une partie de cette puissance.

KVC
Dans Xcode, crez un nouveau projet de type Cocoa Application et nommez-le KVCFun. Ajoutez au projet un nouveau chier de type Objective-C Class. Nommez la classe AppController. ce stade de notre exploration de KVC, nous avons simplement besoin de crer une instance dAppController avec le chier MainMenu.nib. Faites glisser un objet personnalis et xez sa classe AppController (voir Figure 7.1).

Figure 7.1
Crer AppController.

Enregistrez le chier nib. Retournez dans Xcode, ouvrez AppController.h et ajoutez une variable dinstance de type int nomme fido :

Chapitre 7

Modles de conception KVC et KVO

115

@interface AppController: NSObject { int fido; } @end

Dans AppController.m, nous allons crer une mthode init qui xe et lit la valeur de fido en utilisant le codage cl-valeur. Cette approche est un peu stupide car elle reprsente un long chemin dtourn pour arriver un rsultat simple. Il sagit simplement dillustrer le propos, non de trouver une solution efcace. Le chemin est dtourn car le codage cl-valeur se fonde sur des objets. Ainsi, au lieu de passer un int, nous devons crer un NSNumber. Ajoutez la mthode suivante dans AppController.m :
- (id)init { [super init]; [self setValue:[NSNumber numberWithInt:5] forKey:@"fido"]; NSNumber *n = [self valueForKey:@"fido"]; NSLog(@"fido = %@", n); return self; }

Le mcanisme de KVC convertira automatiquement le NSNumber en un int avant de lutiliser pour xer la valeur de fido. Compilez et excutez lapplication, mais ne vous attendez pas des miracles. Lorsque la fentre vide apparat, le message "fido = 5" est afch sur la console. Si des mthodes accesseurs pour obtenir et xer la valeur de fido sont dnies, elles seront utilises. Cependant, elles doivent tre nommes de manire adquate. La mthode "get" doit tre nomme fido, et la mthode "set" doit tre nomme setFido:. Notez quil ne sagit pas dune simple convention. Si vous ne donnez pas aux accesseurs des noms standard, ils ne seront pas appels par les mthodes de KVC. Ajoutez fido et setFido: dans AppController.m :
- (int)fido { NSLog(@"-fido va retourner %d", fido); return fido; } - (void)setFido:(int)x { NSLog(@"-setFido: est invoque avec %d", x); fido = x; }

116

Cocoa

Dclarez ces mthodes dans AppController.h :


- (int)fido; - (void)setFido:(int)x;

Compilez et excutez lapplication. Vous remarquerez que les mthodes accesseurs ont t invoques.

Liaisons
Dans Cocoa, de nombreux objets graphiques possdent des liaisons. Si nous lions une cl, comme fido, un attribut de lobjet graphique, comme sa valeur ou sa couleur de texte, la vue synchronise automatiquement ces deux paramtres. Nous allons ajouter un curseur, lier sa valeur fido et voir comment le codage cl-valeur intervient dans leur synchronisation. Ouvrez MainMenu.nib et dposez un curseur dans la fentre. Dans linspecteur Attributes, cochez la case Continuous (voir Figure 7.2).
Figure 7.2
Crer un curseur continu.

Cocher Continuous

Dans linspecteur Bindings, liez la valeur (value) du curseur la cl fido de linstance dAppController (voir Figure 7.3).

Chapitre 7

Modles de conception KVC et KVO

117

Figure 7.3
Lier la valeur dun curseur do.

Compilez et excutez lapplication. Vous remarquerez que le curseur obtient sa valeur initiale en invoquant valueForKey:, ce qui dclenche lappel de la mthode fido. Lorsque vous dplacez le curseur, il invoque setValue:forKey: an dactualiser la variable fido, ce qui dclenche lappel de la mthode setFido:.

KVO
Que se passe-t-il si la valeur de fido est modie sans passer par le curseur ? Comment celui-ci peut-il connatre la nouvelle valeur ? Tout cela est possible grce lobservation cl-valeur (KVO, Key-Value Observing). Lorsque le curseur est cr, il indique AppController quil observe sa cl fido. Ds que la valeur de fido est modie par les mthodes accesseurs ou par le codage cl-valeur, AppController envoie un message au curseur pour lui signaler la modication de fido. Ouvrez nouveau MainMenu.nib. Ajoutez un champ de texte Label la fentre et liez sa valeur la cl fido dAppController (voir Figure 7.4). Compilez et excutez lapplication. Vous remarquerez que la mthode setFido: est invoque ds que vous dplacez le curseur. Cela signale au champ de texte que la variable fido a t modie. Le champ de texte appelle valueForKey: pour obtenir la nouvelle valeur de fido. Vous voyez alors linvocation de la mthode fido.

118

Cocoa

Figure 7.4
Lier la valeur dun champ de texte do.

Crer des cls observables


la section prcdente, nous avons expliqu que lutilisation des accesseurs ou de KVC pour changer la valeur dune cl permet dinvoquer automatiquement les observateurs pour leur signaler la modication. Que se passe-t-il si nous modions directement la variable ? Ouvrez AppController.h et dclarez une nouvelle mthode daction :
- (IBAction)incrementFido:(id)sender;

Dans AppController.m, implmentez cette mthode :


- (IBAction)incrementFido:(id)sender { fido++; NSLog(@"fido vaut prsent %d", fido); }

Ouvrez MainMenu.nib. Ajoutez un bouton la fentre, intitulez-le Incrmenter Fido et faites glisser le bouton, en maintenant la touche Contrle enfonce, vers linstance dAppController. Le bouton doit dclencher laction incrementFido: (voir Figure 7.5). Vous pourriez esprer quun clic sur le bouton ajustera le curseur et que le champ de texte se mettra jour tout seul. Malheureusement, ce nest pas le cas. Compilez et excutez lapplication pour essayer.

Chapitre 7

Modles de conception KVC et KVO

119

Figure 7.5
Fixer la cible et laction dun bouton.

Si la variable est modie directement, il faut le signaler explicitement aux observateurs. Modiez la mthode incrementFido: :
- (IBAction)incrementFido:(id)sender { [self willChangeValueForKey:@"fido"]; fido++; NSLog(@"fido vaut prsent %d", fido); [self didChangeValueForKey:@"fido"]; }

Compilez et excutez lapplication. Le bouton Incrmenter Fido doit avoir le comportement attendu. Deux autres solutions fonctionnent galement. Tout dabord, il est possible dutiliser le codage cl-valeur :
- (IBAction)incrementFido:(id)sender { NSNumber *n = [self valueForKey:@"fido"]; NSNumber *npp = [NSNumber numberWithInt:[n intValue] + 1]; [self setValue:npp forKey:@"fido"]; }

Ou bien la mthode accesseur pour modier fido :


- (IBAction)incrementFido:(id)sender { [self setFido:[self fido] + 1]; }

Saisissez cette version, puis testez-la.

120

Cocoa

La Figure 7.6 illustre le graphe dobjets que nous avons cr. Les demi-ches reprsentent les liaisons.
Figure 7.6
Graphe dobjets.
NSSlider NSTextField

value: do

value: do

AppController int do - incrementFido: target NSButton action = incrementFido:

Proprits et leurs attributs


Vous le constatez, nous passons beaucoup de temps appeler des mthodes accesseurs. Dans Objective-C 2.0, Apple donne aux programmeurs la possibilit dappeler des accesseurs en utilisant la notation point. Si nous avons un pointeur personne vers un objet qui offre la mthode "get" nomme naissance, nous pouvons linvoquer de la manire suivante :
NSLog(@"La date de naissance de personne est %@", personne.naissance);

Voici comment appeler setNaissance: :


personnes.naissance = [NSDate date];

Je pense que cet ajout au langage est un tantinet stupide car la syntaxe denvoi des messages existe dj. Je ne lutiliserai donc pas dans cet ouvrage. Revenons lcriture des mthodes accesseurs. Si notre objet possde douze variables dinstance, devons-nous crire douze mthodes "set" et douze mthodes "get" ? @property et @synthesize Dans Objective-C 2.0, Apple a propos une manire trs lgante dliminer toute cette quantit de code. Dans le chier AppController.h, remplacez la dclaration des mthodes fido et setFido: par la dclaration dune proprit :
@interface AppController: NSObject { int fido; } @property(readwrite, assign) int fido; @end

Chapitre 7

Modles de conception KVC et KVO

121

Cette seule ligne quivaut la dclaration des mthodes setFido: et fido. Dans AppController.m, nous pouvons utiliser @synthesize pour implmenter les mthodes accesseurs. Supprimez les mthodes fido et setFido:, puis remplacez-les par la ligne suivante :
@synthesize fido;

Vous remarquerez que tout fonctionne comme auparavant. Naturellement, les messages ne sont plus consigns sur la console. Attributs dune proprit De manire gnrale, une proprit est dclare de la manire suivante :
@property (attributs) nom de type;

Les attributs peuvent tre readwrite (par dfaut) ou readonly. Lorsquune proprit est marque readonly, aucune mthode "set" nest dnie. Pour dcrire le fonctionnement de la mthode "set", les attributs peuvent galement inclure lun des mots cls suivants : assign, retain ou copy. Voici leur rle :
m

assign (par dfaut) gnre une simple affectation. La nouvelle valeur nest pas garde. Si nous manipulons un objet et si nous nutilisons pas le ramasse-miettes, assign ne fera sans doute pas laffaire. retain libre lancienne valeur et garde la nouvelle. Cet attribut est utilis uniquement pour les proprits de type objet. Si nous utilisons le ramasse-miettes, assign et retain sont quivalents. copy cre une copie de la nouvelle valeur et laffecte la variable. Cet attribut semploie gnralement pour les proprits de type chane de caractres.

Enn, les attributs peuvent galement inclure le mot cl nonatomic. Si lapplication est multithread, il est parfois important que les mthodes "set" soient atomiques. Autrement dit, lexcution de la mthode "set" depuis un thread nentrera pas en conit avec lexcution de la mme mthode partir dun autre thread. Par dfaut, la directive @synthesize gnre des accesseurs ayant cette proprit. Lorsque lapplication nutilise pas le ramasse-miettes, cela implique la gnration dun verrou pour quun seul thread la fois excute la mthode "set". La gestion des verrous cre un surcot. Lorsque les accesseurs dune proprit nont pas besoin dtre atomiques, il est possible dliminer ce surcot en ajoutant nonatomic aux attributs.

122

Cocoa

Pour les plus curieux : chemin de cl


Les objets sont souvent organiss en rseau. Par exemple, une personne pourrait tre marie une femme qui possde un scooter dun certain modle (voir Figure 7.7).
Figure 7.7
Des objets composent un graphe direct.

Pour obtenir le modle du scooter de lpouse de la personne choisie, nous pouvons utiliser un chemin de cl :
NSString *nm; nm = [personneChoisie valueForKeyPath:@"epouse.scooter.nomModele"];

Nous disons que epouse et scooter sont des relations de la classe Personne et que nomModele est un attribut de la classe Scooter. Les chemins de cls peuvent galement inclure des oprateurs. Par exemple, sil existe un tableau dobjets Personne, nous pouvons obtenir la moyenne des augmentations espres laide des chemins de cls :
NSNumber *moyenne; moyenne = [employes valueForKeyPath:@"@avg.augmentationAttendue"];

Voici les oprateurs les plus utiliss :


@avg @count @max @min @sum

Puisque vous connaissez prsent les chemins de cls, nous pouvons crer des liaisons par programmation. Supposons que nous ayons un champ de texte et que nous voulions y afcher la moyenne des augmentations attendues obtenue partir des objets afchs par un contrleur de tableau. Nous pouvons crer la liaison suivante :
[champTexte bind:@"value" toObject:controleurEmployes withKeyPath:@"arrangedObjects.@avg.augmentationAttendue" options:nil];

Bien entendu, il est gnralement plus facile de crer une liaison depuis Interface Builder. Pour retirer la liaison, utilisez la mthode unbind: :
[champTexte unbind:@"value"];

Chapitre 7

Modles de conception KVC et KVO

123

Pour les plus curieux : KVO


Comment le champ de texte devient-il un observateur pour la cl fido dans lobjet AppController ? Lorsquil est ressuscit depuis le chier nib, le champ de texte sajoute lui-mme en tant quobservateur. Si nous voulons devenir un observateur de cette cl, nous devons crire une ligne de code semblable la suivante :
[appController addObserver:self forKeyPath:@"fido" options:NSKeyValueObservingOld context:somePointer];

Cette mthode est dnie dans NSObject. Cest notre faon de dire "envoie-moi un message ds que la valeur de fido change". Les options et le contexte dterminent les donnes supplmentaires jointes au message. La mthode dclenche ressemble celle-ci :
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { ... }

Dans ce cas, keyPath doit tre @"fido" et object doit tre lAppController. context est le pointeur somePointer fourni comme contexte lorsque nous sommes devenus un observateur. Le dictionnaire change est un ensemble de couples cl-valeur qui peut contenir lancienne valeur de fido et/ou la nouvelle.

8
NSArrayController
Au sommaire de ce chapitre
Dbuter lapplication RaiseMan Codage cl-valeur et nil Ajouter le tri Pour les plus curieux : trier sans NSArrayController

Dans la programmation oriente objet, le motif de conception Modle-Vue-Contrleur (MVC) est trs rpandu. Dans ce motif, chaque classe crite doit entrer dans lune des catgories suivantes :
m

Modle. Les classes de la catgorie Modle dcrivent les donnes. Par exemple, si nous dveloppions des systmes bancaires, nous aurions probablement crer une classe modle nomme CompteEpargne qui dtiendrait une liste des oprations et le solde courant. Les meilleures classes modles ne comprennent rien qui concerne linterface utilisateur et peuvent tre employes dans plusieurs applications. Vue. Les classes de la catgorie Vue font partie de linterface graphique utilisateur. Par exemple, NSSlider est une classe vue. Les meilleures classes vues sont des classes usage gnral qui peuvent tre utilises dans plusieurs applications. Contrleur. Les classes de la catgorie Contrleur sont propres chaque application et sont responsables de la gestion du ux de lapplication. Si lutilisateur doit consulter des donnes, un objet contrleur lit le modle depuis un chier ou une base de donnes, puis lafche laide de la vue. Lorsque lutilisateur modie des donnes, la vue informe le contrleur, qui actualise le modle. Le contrleur enregistre galement les donnes dans le systme de chiers ou la base de donnes.

126

Cocoa

Jusqu Mac OS X 10.3, les programmeurs Cocoa crivaient normment de code dans leurs contrleurs pour les changes de donnes entre les modles et les vues. Pour faciliter lcriture de ce type de contrleurs, Apple a introduit la classe NSController et les liaisons.
NSController est une classe abstraite (voir Figure 8.1). NSObjectController, une sousclasse de NSController, afche le contenu dun objet. NSArrayController est un contrleur dont le contenu est un tableau dobjets de donnes. Dans cet exercice, nous utiliserons un NSArrayController.
Figure 8.1
Classes contrleurs.

Dbuter lapplication RaiseMan


Dans les chapitres venir, nous allons crer une application complte qui permettra de suivre les employs et laugmentation attribue chacun la n de lanne. Au fur et mesure, nous ajouterons lenregistrement des donnes, lannulation dune action, les prfrences de lutilisateur et limpression. la n de ce chapitre, lapplication ressemblera celle illustre la Figure 8.2.
Figure 8.2
Lapplication termine.

Chapitre 8

NSArrayController

127

Les programmeurs Cocoa expriments savent quune telle application peut tre crite avec CoreData. Cependant, je veux montrer comment le faire manuellement. Ensuite, CoreData ne semblera plus aussi magique. Dans Xcode Crez un nouveau projet, de type Cocoa Document-based Application et de nom RaiseMan. Quest-ce quune application base sur les documents ? Il sagit dune application dans laquelle plusieurs documents peuvent tre ouverts simultanment. TextEdit, par exemple, est une application de ce type, contrairement Prfrences Systme. Le chapitre suivant reviendra plus en dtail sur ce type dapplication. Le graphe dobjets de lapplication est prsent la Figure 8.3. Les colonnes dune table sont connectes au NSArrayController par des liaisons, non par des outlets.
Figure 8.3
Graphe dobjets.
NSTableColumn NSTableColumn

value = arrangedObjects.expectedRaise value=arrangedObjects.personName NSMutableArray NSButton action = remove: target NSArrayController Person NSString *personName oat expectedRaise

enable

d=

mo canRe

ve

contentArray = employees employees MyDocument

Person NSString *personName oat expectedRaise

target NSButton action = add:

La classe MyDocument, une sous-classe de NSDocument, a dj t cre pour nous. Lobjet de document est responsable de la lecture et de lcriture des chiers. Dans cet exercice, nous utiliserons un NSArrayController et des liaisons pour construire notre interface simple. Pour le moment, nous najouterons aucun code MyDocument. Pour crer une nouvelle classe Person, slectionnez File > New File.... Parmi tous les choix proposs, choisissez Objective-C Class. Nommez le nouveau chier Person.m et vriez quun chier Person.h est galement cr (voir Figure 8.4).

128

Cocoa

Figure 8.4
Crer une classe Person.

Modiez le chier Person.h pour dclarer deux proprits :


#import <Foundation/Foundation.h> @interface Person: NSObject { NSString *personName; float expectedRaise; } @property (readwrite, copy) NSString *personName; @property (readwrite) float expectedRaise; @end

Ensuite, implmentez ces mthodes dans Person.m et rednissez init et dealloc :


#import "Person.h" @implementation Person - (id)init { [super init]; expectedRaise = 5.0; personName = @"Nouvelle personne"; return self; } - (void)dealloc { [personName release]; [super dealloc]; } @synthesize personName; @synthesize expectedRaise; @end

Chapitre 8

NSArrayController

129

Person est une classe modle elle ne contient aucune information concernant linterface utilisateur. En tant que telle, elle na pas besoin de tout savoir sur les frameworks Cocoa. Par consquent, au lieu dimporter Cocoa/Cocoa.h, nous importons Foundation/Foundation.h. Les deux approches fonctionnent, mais importer le plus petit framework est plus lgant. Cela indique, par exemple, que cette classe peut tre rutilise dans un outil en ligne de commande.

Dans MyDocument.h, dclarez le tableau employees qui contiendra des instances de la classe Person :
@interface MyDocument: NSDocument { NSMutableArray *employees; } - (void)setEmployees:(NSMutableArray *)a;

Dans MyDocument.m, crez la mthode setEmployees:. Dans dealloc, utilisez-la pour librer le tableau :
- (id)init { [super init]; employees = [[NSMutableArray alloc] init]; return self; } - (void)dealloc { [self setEmployees:nil]; [super dealloc]; } - (void)setEmployees:(NSMutableArray *)a { // Cette mthode "set" est inhabituelle. Nous lui ajouterons // beaucoup dintelligence au chapitre suivant. if (a == employees) return; [a retain]; [employees release]; employees = a; }

Dans Interface Builder Dans Xcode, double-cliquez sur MyDocument.nib pour louvrir dans Interface Builder. Supprimez le champ de texte Your document contents here. Faites glisser une vue tableau et deux boutons dans la fentre. Modiez les intituls et organisez-les comme lillustre la Figure 8.5.

130

Cocoa

Figure 8.5
Fentre de document.

Depuis la catgorie Cocoa > Objects & Controllers > Controllers, faites glisser un NSArrayController dans la fentre du chier nib. Dans linspecteur Attributes, xez Class Name Person. Ajoutez des cls pour personName et expectedRaise (voir Figure 8.6).
Figure 8.6
Classes contrleur.

Liez le Content Array du contrleur de tableau au tableau employees de Files Owner, qui est une instance de MyDocument (voir Figure 8.7). La premire colonne de la vue tableau afchera le nom de chaque employ. Cliquez puis double-cliquez sur la colonne pour la slectionner. Dans ce livre, aucun moment nous ne crerons une liaison avec la vue dlement, la vue tableau ou la cellule.

Chapitre 8

NSArrayController

131

Figure 8.7
Lier le Content Array.

Ces oprations sont des erreurs frquentes. Vous devez donc garder un il sur lintitul de la fentre de linspecteur. Dans linspecteur Bindings, xez value de manire afcher la valeur de personName darrangedObjects du NSArrayController (voir Figure 8.8).
Figure 8.8
Lier la colonne personName.

La seconde colonne de la vue tableau afche laugmentation attendue de chaque employ. Faites glisser un formateur de nombres (depuis Library > Cocoa > Views & Cells > Formatters) dans la cellule de la colonne. Dans linspecteur, congurez le formateur pour quil afche le nombre sous forme de pourcentage (voir Figure 8.9).

132

Cocoa

Figure 8.9
Ajouter un formateur de nombres.

Slectionnez nouveau la seconde colonne. Dans linspecteur Bindings, xez value de manire afcher la valeur dexpectedRaise darrangedObjects du NSArrayController (voir Figure 8.10).
Figure 8.10
Lier la seconde colonne expectedRaise darrangedObjects.

En maintenant la touche contrle enfonce, faites glisser le bouton Ajouter un nouvel employ vers le contrleur de tableau pour quil devienne sa cible. Fixez laction add:. Procdez de mme pour le bouton Supprimer, en xant laction remove:. Dans linspecteur Bindings, liez lattribut enabled du bouton lattribut canRemove du NSArrayController (voir Figure 8.11). Lutilisateur voudra galement pouvoir supprimer les employs slectionns en appuyant sur la touche Suppr de son clavier. Slectionnez le bouton, puis, dans linspecteur Attributes, xez lquivalent clavier (Key Equiv.) la touche Suppr (voir Figure 8.12).

Chapitre 8

NSArrayController

133

Figure 8.11
Lier lattribut enabled au bouton de suppression.

Figure 8.12
La touche Suppr doit activer le bouton de suppression.

Compilez et excutez lapplication. Vous devez pouvoir crer et supprimer des objets Person. Vous devez galement pouvoir modier les attributs des objets Person en utilisant la vue tableau. Enn, vous devez tre en mesure douvrir plusieurs documents sans titre. Non, vous ne pouvez pas enregistrer ces documents dans des chiers ; ce sera pour bientt.

Codage cl-valeur et nil


Vous aurez remarqu que notre exemple contient trs peu de code. Nous avons dcrit ce qui doit tre afch dans chaque colonne laide dInterface Builder, mais aucun code nappelle les mthodes accesseurs de la classe Person. Comment cela peut-il fonctionner ? La rponse tient dans le codage cl-valeur (KVC, Key-Value Coding), qui permet dobtenir des classes gnriques utilisables, comme NSArrayController.

134

Cocoa

Les mthodes KVC se chargent automatiquement des conversions de type. Par exemple, lorsque lutilisateur saisit une nouvelle augmentation, le formateur cre une instance de NSNumber. La mthode KVC setValue:forKey: le convertit automatiquement en un type float avant dinvoquer setExpectedRaise:. Ce fonctionnement est extrmement pratique. Cependant, la conversion dun NSDecimalNumber * en un float pose un problme : les pointeurs peuvent tre nil, contrairement aux float. Si setValue:forKey: reoit une valeur nil qui doit tre convertie en un type non pointeur, elle appelle une mthode spcique :
- (void)setNilValueForKey:(NSString *)s

Cette mthode, dnie dans NSObject, lance une exception. Ainsi, si lutilisateur laisse le champ de laugmentation vide, lobjet lance une exception. En gnral, la mthode setNilValueForKey: est rednie an de donner une valeur par dfaut la variable dinstance. Dans notre exemple, nous allons rednir cette mthode dans la classe Person et xer expectedRaise 0.0. Ajoutez la mthode suivante dans Person.m :
- (void)setNilValueForKey:(NSString *)key { if ([key isEqual:@"expectedRaise"]) { [self setExpectedRaise:0.0]; } else { [super setNilValueForKey:key]; } }

Lorsquun formateur a t attribu, comme nous lavons fait, il est parfois inutile de rednir setNilValueForKey: car le formateur, en fonction de sa conguration, peut empcher la saisie de valeurs nil.

Ajouter le tri
Pendant que lapplication sexcute, cliquez sur les en-ttes des colonnes. Le tri fonctionne, mais ne correspond pas forcment ce que nous attendons. En effet, le tri se fonde sur la mthode compare:, qui tient compte de la casse de manire un peu particulire. Ainsi, "Z" vient avant "a". Changeons la mthode utilise pour le tri. Ouvrez MyDocument.nib. Les critres de tri se dnissent dans linspecteur Attributes de chaque colonne. Les utilisateurs pourront choisir lattribut qui servira au tri des donnes, en cliquant sur len-tte de la colonne qui contient cet attribut. Slectionnez la colonne qui afche personName. Dans linspecteur, xez Sort Key personName et Selector caseInsensitiveCompare: (voir Figure 8.13).

Chapitre 8

NSArrayController

135

Figure 8.13
Trier sur la base de personName.

La mthode caseInsensitiveCompare: est dnie par NSString. Voici un exemple dutilisation :


NSString *x = @"Piaggio"; NSString *y = @"Italjet" NSComparisonResult result = [x caseInsensitiveCompare:y]; // x viendra-t-il en premier dans le dictionnaire? if (result == NSOrderedAscending) { ... }

NSComparisonResult est un simple entier. NSOrderedAscending vaut -1, NSOrderedSame vaut 0 et NSOrderedDescending vaut 1.

Compilez et excutez lapplication. Cliquez sur len-tte de la colonne pour trier les donnes. Cliquez nouveau sur len-tte pour obtenir les donnes en ordre inverse.

Pour les plus curieux : trier sans NSArrayController


Au Chapitre 6, nous avons cr une vue tableau en implmentant explicitement les mthodes dataSource. Vous vous demandez peut-tre comment nous pourrions implmenter le tri dans notre application. Les informations ajoutes aux colonnes sont rassembles dans un tableau dobjets NSSortDescriptor. Un descripteur de tri comprend la cl, un slecteur et un indicateur dordre de tri des donnes (croissant ou dcroissant). Si nous disposons dun NSMutableArray dobjets, nous pouvons employer la mthode suivante pour le trier :
- (void)sortUsingDescriptors:(NSArray *)sortDescriptors

136

Cocoa

Une mthode dataSource facultative de la vue tableau est invoque lorsque lutilisateur clique sur len-tte dune colonne disposant dun descripteur de tri :
- (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)oldDescriptors

Si nous avons un tableau modiable qui contient les informations dune vue tableau, nous pouvons implmenter la mthode de la manire suivante :
- (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)oldDescriptors { NSArray *newDescriptors = [tableView sortDescriptors]; [myArray sortUsingDescriptors:newDescriptors]; [tableView reloadData]; }

Et voil, le tri a t ajout lapplication.

Exercice 1
Modiez lapplication an que les personnes soient tries en fonction de la longueur de leur nom. Vous pouvez raliser cet exercice uniquement partir dInterface Builder ; lastuce est dutiliser un chemin de cl. Conseil : les chanes de caractres offrent une mthode length.

Exercice 2
Dans la premire dition de ce livre, lapplication RaiseMan nutilisait pas un NSArrayController ni le mcanisme des liaisons (ces possibilits ont t ajoutes dans Mac OS X 10.3). Les concepts dcrits dans les chapitres prcdents taient donc employs. Lexercice consiste rcrire lapplication RaiseMan lancienne. Les liaisons semblent souvent quelque peu magiques et il est bon de savoir comment raliser les choses sans avoir recours la magie. Vous devez dmarrer un nouveau projet, car, au chapitre suivant, nous poursuivrons le projet existant. La classe Person ne sera pas modie. Dans MyDocument.nib, lidentiant de chaque colonne sera le nom de la variable afcher. Ensuite, la classe MyDocument sera la source de donnes de la vue tableau et la cible des deux boutons. MyDocument comprendra un tableau dobjets Person quelle afchera. Pour vous permettre de dmarrer, voici le chier MyDocument.h :
#import <Cocoa/Cocoa.h> @class Person; @interface MyDocument: NSDocument

Chapitre 8

NSArrayController

137

{ NSMutableArray *employees; IBOutlet NSTableView *tableView; } - (IBAction)createEmployee:(id)sender; - (IBAction)deleteSelectedEmployees:(id)sender; @end

Voici les parties intressantes de MyDocument.m :


- (id)init { [super init]; employees = [[NSMutableArray alloc] init]; return self; } - (void)dealloc { [employees release]; [super dealloc]; } #pragma mark Action methods - (IBAction)deleteSelectedEmployees:(id)sender { // Quelle est la ligne slectionne? NSIndexSet *rows = [tableView selectedRowIndexes]; // La slection est-elle vide? if ([rows count] == 0) { NSBeep(); return; } [employees removeObjectsAtIndexes:rows]; [tableView reloadData]; } - (IBAction)createEmployee:(id)sender { Person *newEmployee = [[Person alloc] init]; [employees addObject:newEmployee]; [newEmployee release]; [tableView reloadData]; } #pragma mark Table view dataSource methods - (int)numberOfRowsInTableView:(NSTableView *)aTableView { return [employees count]; } - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex {

138

Cocoa

// Quel est lidentifiant de la colonne? NSString *identifier = [aTableColumn identifier]; // Quelle est la personne? Person *person = [employees objectAtIndex:rowIndex]; // Quelle est la valeur de lidentifiant nomm? return [person valueForKey:identifier]; } - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { NSString *identifier = [aTableColumn identifier]; Person *person = [employees objectAtIndex:rowIndex]; // Fixer la valeur de lidentifiant nomm. [person setValue:anObject forKey:identifier]; }

Lorsque votre application sera oprationnelle, noubliez pas dajouter le tri !

9
NSUndoManager
Au sommaire de ce chapitre

NSInvocation Fonctionnement de NSUndoManager Ajouter lannulation RaiseMan Observation cl-valeur Annuler les modications Commencer la modication lors de lajout Pour les plus curieux : fentres et gestionnaire dannulation

Grce NSUndoManager, nous pouvons ajouter des possibilits dannulation nos applications, et ce de manire trs lgante. Lorsque des objets sont ajouts, modis ou supprims, le gestionnaire dannulation conserve une trace de tous les messages qui doivent tre envoys pour annuler ces actions. Lorsque le mcanisme dannulation est invoqu, le gestionnaire dannulation conserve une trace de tous les messages qui doivent tre envoys pour rtablir ces actions. Ce mcanisme se fonde sur deux piles dobjets NSInvocation. Ce sujet est plutt complexe pour tre prsent si tt dans un livre. Cependant, lannulation interagit avec larchitecture base de documents. En tudiant cet aspect maintenant, nous pourrons voir le fonctionnement de larchitecture base de documents au chapitre suivant.

NSInvocation
Comme vous pouvez limaginer, il est commode dempaqueter un message, y compris le slecteur, le destinataire et tous les arguments, sous forme dun objet pouvant tre invoqu loisir. Un tel objet est une instance de NSInvocation.

140

Cocoa

La retransmission dun message constitue lutilisation la plus pratique des invocations. Lorsquun objet reoit un message quil ne comprend pas, le systme de distribution des messages vrie, avant de lancer une exception, si lobjet implmente la mthode suivante :
- (void)forwardInvocation:(NSInvocation *)x

Dans lafrmative, le message envoy est empaquet sous forme dun NSInvocation et forwardInvocation: est invoque avec cet objet en paramtre.

Fonctionnement de NSUndoManager
Supposons que lutilisateur ouvre un nouveau document RaiseMan et procde trois modications :
m m m

insrer un nouvel enregistrement ; changer le nom, de "Nouvelle personne" "Rex Fido" ; changer laugmentation, de 0 20.

Au cours de chaque modication, le contrleur ajoute dans la pile des annulations une invocation qui permettra dannuler cette modication. Pour simplier le propos, nous dirons "linverse de la modication est ajout la pile des annulations". La Figure 9.1 illustre la pile des annulations aprs ces trois modications.
Figure 9.1
La pile des annulations.

Si lutilisateur clique sur lentre de menu Undo (annuler), la premire invocation est retire de la pile, puis appele. Laugmentation de la personne est alors remise zro.

Chapitre 9

NSUndoManager

141

Si lutilisateur clique nouveau sur Undo, le nom de la personne revient "Nouvelle personne". Chaque fois quun lment est retir de la pile des annulations et invoqu, linverse de lopration dannulation doit tre ajout la pile des rtablissements. Ainsi, aprs avoir annul les deux oprations prcdentes, la pile des annulations et la pile des rtablissements ressembleront celles illustres la Figure 9.2.
Figure 9.2
La pile des annulations revue.

Le gestionnaire dannulation est relativement intelligent. Lorsque lutilisateur effectue des modications, les invocations dannulation vont dans la pile des annulations. Lorsquil annule des modications, les invocations dannulation vont dans la pile des rtablissements. Lorsquil rtablit des modications, les invocations dannulation vont dans la pile des annulations. Toutes ces oprations sont ralises automatiquement pour nous. Notre seul travail consiste fournir au gestionnaire dannulation les invocations inverses appropries. Supposons prsent que nous crivions une mthode nomme chauffer et que linverse de cette mthode soit refroidir. Voici comment mettre en uvre lannulation :
- (void)chauffer { temperature = temperature + 10; [[undoManager prepareWithInvocationTarget:self] refroidir]; [self afficherLesChangementsDeTemperature]; }

La mthode prepareWithInvocationTarget: note la cible et retourne le gestionnaire dannulation. Celui-ci rednit ensuite forwardInvocation: de manire ajouter linvocation de refroidir dans la pile des annulations.

142

Cocoa

Pour terminer lexemple, nous devons implmenter refroidir :


- (void)refroidir { temperature = temperature - 10; [[undoManager prepareWithInvocationTarget:self] chauffer]; [self afficherLesChangementsDeTemperature]; }

Nous avons de nouveau indiqu lopration inverse au gestionnaire dannulation. Si refroidir est invoque suite une annulation, linverse sera plac dans la pile des rtablissements. Les invocations places dans les piles sont regroupes. Par dfaut, toutes les invocations ajoutes dans une pile au cours dun mme vnement forment un groupe. Par consquent, si une action de lutilisateur provoque des modications dans plusieurs objets, elles seront toutes annules par un seul clic sur lentre de menu Undo. Le gestionnaire dannulation peut galement modier lintitul des entres de menu Undo et Redo. Par exemple, Undo Insertion est plus explicite que Undo. Voici comment modier ltiquette de lentre de menu :
[undoManager setActionName:@"Insertion"];

Comment obtenons-nous un gestionnaire dannulation ? Nous pouvons en crer un explicitement, mais chaque instance de NSDocument possde dj son propre gestionnaire dannulation.

Ajouter lannulation RaiseMan


Donnons lutilisateur la possibilit dannuler les effets dun clic sur les boutons Ajouter un nouvel employ et Supprimer, ainsi que la possibilit dannuler les modications apportes aux objets Person dans le tableau. Le code ncessaire ira dans la classe MyDocument. Lorsque je conois une classe, jai tendance imaginer mes variables dinstance avec lun des quatre rles suivants : 1. Attributs simples. Par exemple, chaque tudiant possde un prnom. Les attributs simples sont gnralement des nombres ou des instances de NSString, NSDate ou NSData. 2. Relations " un". Par exemple, chaque tudiant est li une cole. Cela ressemble un attribut simple, mais le type est un objet complexe, non un type de base. Les relations " un" sont implmentes laide de pointeurs. Une instance de Student possde un pointeur sur une instance de School.

Chapitre 9

NSUndoManager

143

3. Relations " plusieurs" ordonnes. Par exemple, chaque liste de lecture possde une liste de titres. Les chansons sont donnes dans un ordre prcis. Les relations de ce type sont gnralement implmentes laide dun NSMutableArray. 4. Relations " plusieurs" non ordonnes. Par exemple, chaque service possde un ensemble demploys. Ces employs peuvent tre afchs dans un ordre particulier, par exemple en fonction de leur nom de famille, mais il nest pas inhrent la relation. Les relations de ce type sont gnralement implmentes laide dun NSMutableSet. Nous avons vu prcdemment comment dnir des attributs simples et des relations " un" en utilisant le codage cl-valeur. Il ne faut pas oublier que laccs une valeur, par exemple de fido, se fera avec les accesseurs, sils existent. De mme, nous pouvons crer des accesseurs pour les relations " plusieurs" ordonnes ou non. Par exemple, supposons quune instance de PlayList utilise un NSMutableArray dobjets Song. Pour manipuler ce tableau laide de KVC, nous devons demander la liste de lecture son mutableArrayValueForKey:. Nous obtenons alors un objet mandataire (proxy). Cet objet sait quil reprsente le tableau qui contient les chansons.
id arrayProxy = [playlist mutableArrayValueForKey:@"songs"]; int songCount = [arrayProxy count];

Dans cet exemple, lorsque nous demandons accs la valeur de count de lobjet mandataire, celui-ci demande lobjet PlayList sil possde une mthode countOfSongs. Dans lafrmative, il appelle la mthode et retourne le rsultat. Dans le cas contraire, il reoit le tableau des chansons et lui demande sa valeur de count (voir Figure 9.3). Le nom de la mthode countOfSongs nest pas simplement une convention. Le mcanisme du codage cl-valeur recherche une mthode au nom correctement form.
Figure 9.3
Codage cl-valeur pour les relations " plusieurs" ordonnes.
count (1) Array Proxy key = "songs" Song NSMutableArray countOfSongs (2) Song Song

PlayList - countOfSongs

144

Cocoa

Il existe plusieurs cas, dont voici la liste :


id arrayProxy = [playlist mutableArrayValueForKey:@"songs"]; int x = [arrayProxy count]; // est identique int x = [playlist countOfSongs]; // si countOfSongs existe. id y = [arrayProxy objectAtIndex:5] // est identique id y = [playlist objectInSongsAtIndex:5]; // si la mthode existe. [arrayProxy insertObject:p atIndex:4] // est identique [playlist insertObject:p inSongsAtIndex:4]; // si la mthode existe. [arrayProxy removeObjectAtIndex:3] // est identique [playlist removeObjectFromSongsAtIndex:3] // si la mthode existe.

Pour les relations " plusieurs" non ordonnes, nous disposons dun ensemble dappels semblables (voir Figure 9.4).
Figure 9.4
Codage cl-valeur pour les relations " plusieurs" non ordonnes.
count (1) Set Proxy key = "students" Student NSMutableSet countOfStudents (2) Student Student

Teacher - countOfStudents

id setProxy = [teacher mutableSetValueForKey:@"students"]; int x = [setProxy count]; // est identique int x = [teacher countOfStudents]; // si countOfStudents existe. [setProxy addObject:newStudent]; // est identique [teacher addStudentsObject:newStudent]; // si la mthode existe. [setProxy removeObject:expelledStudent]; // est identique [teacher removeStudentsObject:expelledStudent]; // si la mthode existe.

Puisque nous avons li le contentArray du contrleur de tableau au tableau employees de lobjet MyDocument, le contrleur utilisera le codage cl-valeur pour ajouter et retirer des objets Person. Nous allons en tirer prot pour ajouter des invocations dannulation dans la pile correspondante lorsque les personnes sont ajoutes et retires. Ajoutez les mthodes suivantes dans MyDocument.m :

Chapitre 9

NSUndoManager

145

- (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index { NSLog(@"ajout de %@ %@", p, employees); // Ajouter lopration inverse dans la pile des annulations. NSUndoManager *undo = [self undoManager]; [[undo prepareWithInvocationTarget:self] removeObjectFromEmployeesAtIndex:index]; if (![undo isUndoing]) { [undo setActionName:@"Insrer la personne"]; } // Ajouter la personne dans le tableau. [employees insertObject:p atIndex:index]; } - (void)removeObjectFromEmployeesAtIndex:(int)index { Person *p = [employees objectAtIndex:index]; NSLog(@"suppression de %@ de %@", p, employees); // Ajouter lopration inverse dans la pile des annulations. NSUndoManager *undo = [self undoManager]; [[undo prepareWithInvocationTarget:self] insertObject:p inEmployeesAtIndex:index]; if (![undo isUndoing]) { [undo setActionName:@"Supprimer la personne"]; } [employees removeObjectAtIndex:index]; }

Ces mthodes seront appeles automatiquement lorsque le NSArrayController voudra insrer ou supprimer des objets Person, par exemple lorsque les boutons dajout et de suppression lui envoient les messages insert: et remove:. Dclarez ces mthodes dans MyDocument.h :
- (void)removeObjectFromEmployeesAtIndex:(int)index; - (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index;

Puisque nous utilisons la classe Person, nous devons en informer le compilateur au dbut de MyDocument.h :
#import <Cocoa/Cocoa.h> @class Person;

Nous devons galement importer Person.h au dbut de MyDocument.m :


#import "Person.h"

Nous venons de mettre en place lannulation des suppressions et des insertions. Lannulation des modications sera un peu plus complexe. Avant de nous atteler cette tche, compilez et excutez lapplication. Testez les possibilits dannulation disponibles. Vous remarquerez que le rtablissement fonctionne galement.

146

Cocoa

Observation cl-valeur
Au Chapitre 7, nous avons prsent le codage cl-valeur. Pour rappel, KVC est une manire de lire et de changer la valeur dune variable en utilisant son nom. Lobservation cl-valeur (KVO, Key-Value Observing) nous permet dtre informs des modications. Pour mettre en uvre lannulation des modications, notre objet document doit tre inform des modications apportes aux cls expectedRaise et personName pour tous les objets Person quil contient. Une mthode de NSObject nous permet de nous enregistrer comme observateur de ces modications :
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context

Le paramtre observer prcise lobjet qui doit tre inform des changements apports un certain chemin de cl (keyPath). La variable options indique quelles informations concernant la modication seront fournies lobservateur. Par exemple, elles peuvent contenir lancienne valeur (avant la modication) et la nouvelle valeur (aprs la modication). La variable context est un pointeur sur les donnes qui doivent tre envoyes avec les autres informations. Elles sont quelconques et peuvent servir nimporte quels objectifs. En gnral, je la laisse NULL. Lorsquun changement se produit, lobservateur reoit le message suivant :
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

Lobservateur est inform du chemin de cl qui a chang, ainsi que de lobjet concern. change est un dictionnaire qui, selon les options indiques lors de linscription en tant quobservateur, peut contenir lancienne et/ou la nouvelle valeur. Bien entendu, le pointeur context indiqu au moment de cet enregistrement est galement fourni. En gnral, je lignore.

Annuler les modications


La premire tape consiste enregistrer lobjet document comme observateur des modications apportes ses objets Person. Ajoutez les mthodes suivantes dans MyDocument.m :
- (void)startObservingPerson:(Person *)person { [person addObserver:self forKeyPath:@"personName"

Chapitre 9

NSUndoManager

147

options:NSKeyValueObservingOptionOld context:NULL]; [person addObserver:self forKeyPath:@"expectedRaise" options:NSKeyValueObservingOptionOld context:NULL]; } - (void)stopObservingPerson:(Person *)person { [person removeObserver:self forKeyPath:@"personName"]; [person removeObserver:self forKeyPath:@"expectedRaise"]; }

Elles sont appeles chaque fois quun objet Person rejoint ou quitte le document :
- (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index { // Ajouter lopration inverse dans la pile des annulations. NSUndoManager *undo = [self undoManager]; [[undo prepareWithInvocationTarget:self] removeObjectFromEmployeesAtIndex:index]; if (![undo isUndoing]) { [undo setActionName:@"Insrer la personne"]; } // Ajouter la personne dans le tableau. [self startObservingPerson:p]; [employees insertObject:p atIndex:index]; } - (void)removeObjectFromEmployeesAtIndex:(int)index { Person *p = [employees objectAtIndex:index]; // Ajouter lopration inverse dans la pile des annulations. NSUndoManager *undo = [self undoManager]; [[undo prepareWithInvocationTarget:self] insertObject:p inEmployeesAtIndex:index]; if (![undo isUndoing]) { [undo setActionName:@"Supprimer la personne"]; } [self stopObservingPerson:p]; [employees removeObjectAtIndex:index]; } - (void)setEmployees:(NSMutableArray *)a { if (a == employees) return; for (Person *person in employees) { [self stopObservingPerson:person]; }

148

Cocoa

[a retain]; [employees release]; employees = a; for (Person *person in employees) { [self startObservingPerson:person]; } }

Implmentons prsent la mthode qui effectue les modications, ainsi que son rle inverse :
- (void)changeKeyPath:(NSString *)keyPath ofObject:(id)obj toValue:(id)newValue { // setValue:forKeyPath: provoquera lappel de la mthode KVO, // qui prend en charge les annulations. [obj setValue:newValue forKeyPath:keyPath]; }

Implmentons la mthode qui sera invoque ds quun objet Person est modi, soit par lutilisateur, soit par la mthode changeKeyPath:ofObject:toValue:. Elle place un appel changeKeyPath:ofObject:toValue: sur la pile avec lancienne valeur de la cl modie.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSUndoManager *undo = [self undoManager]; id oldValue = [change objectForKey:NSKeyValueChangeOldKey]; // Les objets NSNull servent reprsenter nil dans un dictionnaire. if (oldValue == [NSNull null]) { oldValue = nil; } NSLog(@"ancienne valeur = %@", oldValue); [[undo prepareWithInvocationTarget:self] changeKeyPath:keyPath ofObject:object toValue:oldValue]; [undo setActionName:@"Modification"]; }

Cela devrait aller. Aprs avoir compil lapplication, les fonctions annuler et rtablir devraient tre parfaitement oprationnelles. Lorsquune modication est apporte au document, un point apparat dans le bouton rouge de fermeture situ dans la barre de titre de la fentre. Il indique quun changement a t apport au document sans que celui-ci ait t enregistr. Au chapitre suivant, nous verrons comment enregistrer les documents dans des chiers.

Chapitre 9

NSUndoManager

149

Commencer la modication lors de lajout


Lapplication fonctionne parfaitement, mais les utilisateurs se plaignent : "Pourquoi dois-je double-cliquer dans le champ de texte pour commencer la modication aprs avoir ajout une personne ? Je vais videmment changer immdiatement le nom de la nouvelle personne. Pourriez-vous passer en mode modication au moment de lajout ?" Assez bizarrement, ce nest pas aussi simple quil y parat. Je vais donc donner le code ncessaire. Tout dabord, MyDocument.h aura besoin dune action et de deux variables dinstance :
@interface MyDocument: NSDocument { NSMutableArray *employees; IBOutlet NSTableView *tableView; IBOutlet NSArrayController *employeeController; } - (IBAction)createEmployee:(id)sender;

Enregistrez ce chier ; vous devez toujours enregistrer un chier .h pour que ses outlets et ses actions soient visibles dans Interface Builder. Dans Interface Builder, faites glisser, en maintenant la touche Contrle enfonce, le bouton dajout vers Files Owner (qui est linstance de MyDocument). Fixez son action createEmployee: (voir Figure 9.5).

Figure 9.5
Fixer la cible et laction du bouton dajout.

Faites un Contrle-clic sur Files Owner et connectez loutlet tableView la vue tableau, puis loutlet employeeController au contrleur de tableau (voir Figure 9.6).

150

Cocoa

Figure 9.6
Fixer les outlets.

Dans MyDocument.m, ajoutez la mthode createEmployee: :


- (IBAction)createEmployee:(id)sender { NSWindow *w = [tableView window]; // Essayer de terminer toute modification en cours. BOOL editingEnded = [w makeFirstResponder:w]; if (!editingEnded) { NSLog(@"Impossible darrter la modification"); return; } NSUndoManager *undo = [self undoManager]; // Une modification a-t-elle dj eu lieu dans cet vnement? if ([undo groupingLevel]) { // Fermer le dernier groupe. [undo endUndoGrouping]; // Ouvrir un nouveau groupe. [undo beginUndoGrouping]; } // Crer lobjet. Person *p = [employeeController newObject]; // Lajouter au tableau de contenu de employeeController [employeeController addObject:p]; [p release]; // Trier nouveau (lutilisateur a pu trier une colonne). [employeeController rearrangeObjects];

Chapitre 9

NSUndoManager

151

// Obtenir le tableau tri. NSArray *a = [employeeController arrangedObjects]; // Rechercher lobjet ajout. int row = [a indexOfObjectIdenticalTo:p]; NSLog(@"starting edit of %@ in row %d", p, row); // Commencer ldition dans la premire colonne. [tableView editColumn:0 row:row withEvent:nil select:YES]; }

Pour le moment, vous ntes pas suppos comprendre chaque ligne de ce code, mais examinez la mthode an den comprendre lesprit. Compilez et excutez lapplication.

Pour les plus curieux : fentres et gestionnaire dannulation


Une vue peut ajouter des modications au gestionnaire dannulation. Par exemple, NSTextView peut placer dans le gestionnaire dannulation chaque modication apporte au texte. Cela peut se faire dans Interface Builder (voir Figure 9.7).
Figure 9.7
Linspecteur de NSTextView.

Comment la vue texte connat-elle le gestionnaire dannulation utiliser ? Tout dabord, elle demande son dlgu. Le dlgu de NSTextView peut implmenter la mthode suivante :
- (NSUndoManager *)undoManagerForTextView:(NSTextView *)tv

152

Cocoa

Ensuite, elle demande sa fentre. Pour cela, NSWindow dispose de la mthode suivante :
- (NSUndoManager *)undoManager

Le dlgu de la fentre peut fournir un gestionnaire dannulation pour la fentre en implmentant la mthode suivante :
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window

Les entres de menu Undo/Redo retent ltat du gestionnaire dannulation pour la fentre active.

10
Archivage
Au sommaire de ce chapitre
NSCoder et NSCoding Larchitecture de document Enregistrer et NSKeyedArchiver Charger et NSKeyedUnarchiver Affecter une extension et une icne au type de chier Pour les plus curieux : empcher les boucles innies Pour les plus curieux : crer un protocole Pour les plus curieux : applications bases sur des documents sans annulation Identiants de type universel

Lorsquun programme orient objet sexcute, un graphe dobjets complexe est cr. Il est souvent ncessaire de reprsenter ce graphe dobjets sous forme dun ux doctets. Cette opration se nomme archivage (voir Figure 10.1). Ce ux doctets peut ensuite tre envoy au travers dune connexion rseau ou crit dans un chier. Par exemple, lorsque vous enregistrez un chier nib, Interface Builder archive des objets dans un chier. Les programmeurs Java appellent ce processus srialisation.
Figure 10.1
Archivage.

154

Cocoa

Pour recrer le graphe des objets partir du ux doctets, nous devons dsarchiver celui-ci. Par exemple, lorsque lapplication dmarre, elle dsarchive les objets prsents dans le chier nib cr par Interface Builder. Mme si les objets possdent la fois des variables et des mthodes dinstance, seules les variables dinstance et le nom de la classe sont placs dans larchive. Autrement dit, seules les donnes, non le code, vont dans larchive. Par consquent, si une application archive un objet et une autre application dsarchive le mme objet, les deux applications doivent contenir le code de la classe. Par exemple, dans le chier nib, nous avons employ des classes comme NSWindow et NSButton issues du framework AppKit. Si lapplication nest pas lie au framework AppKit, elle sera incapable de crer les instances de NSWindow et de NSButton quelle trouve dans larchive. Une publicit pour un shampooing disait "je lai dit deux amis, qui lont dit deux amis, qui lont dit deux amis, et ainsi de suite, et ainsi de suite, et ainsi de suite". Lide tait que, tant que le shampooing tait prsent des amis, toutes les personnes que cela pouvait intresser niraient par utiliser le shampooing. Larchivage dun objet fonctionne de manire semblable. Nous archivons un objet racine, qui archive les objets auxquels il est attach, qui archivent les objets auxquels ils sont attachs, et ainsi de suite, et ainsi de suite, et ainsi de suite. Au nal, chaque objet concern arrivera dans larchive. Larchivage se fait en deux tapes. Premirement, nous devons indiquer aux objets comment sarchiver. Deuximement, nous devons dclencher larchivage. Le langage Objective-C possde une construction appele protocole. Elle est identique la construction Java appele interface. Un protocole est ainsi une liste de dclarations de mthodes. Lorsque nous crons une classe qui implmente un protocole, cette classe promet dimplmenter toutes les mthodes dclares dans le protocole.

NSCoder et NSCoding
NSCoding est lun des protocoles. Si notre classe implmente NSCoding, elle promet dimplmenter les mthodes suivantes : - (id)initWithCoder:(NSCoder *)coder - (void)encodeWithCoder:(NSCoder *)coder NSCoder est une abstraction dun ux doctets. Nous pouvons crire nos donnes dans un codeur ou lire nos donnes depuis un codeur. La mthode initWithCoder: de notre objet lira des donnes partir de codeur et les enregistrera dans les variables dinstance.

Chapitre 10

Archivage

155

La mthode encodeWithCoder: de notre objet lira les variables dinstance et crira leurs valeurs dans le codeur. Dans ce chapitre, nous allons implmenter cette mthode pour notre classe Person.
NSCoder est une classe abstraite. On ne cre jamais dinstances dune classe abstraite. Une classe abstraite possde des caractristiques dont hritent ses sous-classes. On cre des instances des sous-classes concrtes. Plus prcisment, nous utiliserons NSKeyedUnarchiver pour lire des objets depuis un ux de donnes et NSKeyed-Archiver pour crire des objets dans le ux de donnes.

Encoder
NSCoder offre de nombreuses mthodes, mais la plupart des programmeurs nen utilisent que quelques-unes. Voici les mthodes les plus utilises lors de lencodage des donnes par lintermdiaire dun codeur :
- (void)encodeObject:(id)anObject forKey:(NSString *)aKey

Cette mthode crit anObject dans le codeur et lassocie la cl aKey. Elle dclenche linvocation de la mthode encodeWithCoder: danObject (et ils lont dit deux amis, qui lont dit deux amis). Pour chaque type C primitif (par exemple int et float), NSCoder dispose dune mthode dencodage :
(void)encodeBool:(BOOL)boolv forKey:(NSString *)key (void)encodeDouble:(double)realv forKey:(NSString *)key (void)encodeFloat:(float)realv forKey:(NSString *)key (void)encodeInt:(int)intv forKey:(NSString *)key

Pour apporter lencodage notre classe Person, ajoutez la mthode suivante dans Person.m :
- (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:personName forKey:@"personName"]; [coder encodeFloat:expectedRaise forKey:@"expectedRaise"]; }

Si vous examinez la documentation de la classe NSString, vous constaterez quelle implmente le protocole NSCoding. Par consquent, personName sait comment sencoder. Toutes les classes dAppKit et Foundation les plus utilises implmentent le protocole NSCoding, lexception notable de NSObject. Puisquelle drive de NSObject, Person

156

Cocoa

nappelle pas [super encodeWithCoder:coder]. Si la classe mre de Person implmentait le protocole NSCoding, la mthode aurait t la suivante :
- (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeObject:personName forKey:@"personName"]; [coder encodeFloat:expectedRaise forKey:@"expectedRaise"]; }

Lappel de la mthode encodeWithCoder: de la classe mre donne celle-ci la possibilit dcrire ses variables dans le codeur. Ainsi, chaque classe de la hirarchie archive uniquement ses variables dinstance, non celles de sa superclasse. Dcoder Pour le dcodage des donnes partir de codeur, nous disposons de mthodes analogues :
(id)decodeObjectForKey:(NSString *)aKey (BOOL)decodeBoolForKey:(NSString *)key (double)decodeDoubleForKey:(NSString *)key (float)decodeFloatForKey:(NSString *)key (int)decodeIntForKey:(NSString *)key

Si, pour quelque raison que ce soit, le ux ne contient pas les donnes correspondant une cl, nous recevons zro en rsultat. Par exemple, si lobjet na pas crit les donnes qui correspondent la cl toto lorsque le ux a t crit, le codeur retourne 0.0 lorsquon lui demande de dcoder une valeur float pour la cl toto. Si nous lui demandons de dcoder un objet pour la cl toto, il retourne nil. Pour ajouter le dcodage notre classe Person, ajoutez la mthode suivante dans Person.m :
- (id)initWithCoder:(NSCoder *)coder { [super init]; personName = [[coder decodeObjectForKey:@"personName"] retain]; expectedRaise = [coder decodeFloatForKey:@"expectedRaise"]; return self; }

Une fois encore, elle ninvoque pas la mthode initWithCoder: de la classe mre, car NSObject nen possde pas. Si la superclasse de Person implmentait le protocole NSCoding, la mthode serait la suivante :
- (id)initWithCoder:(NSCoder *)coder { [super initWithCoder:coder];

Chapitre 10

Archivage

157

personName = [[coder decodeObjectForKey:@"personName"] retain]; expectedRaise = [coder decodeFloatForKey:@"expectedRaise"]; return self; }

Au Chapitre 3, nous avons prcis que linitialiseur dsign faisait tout le travail et appelait linitialiseur dsign de la classe mre. Par ailleurs, tous les autres initialiseurs invoquent linitialiseur dsign. Puisque Person dnit une mthode init, quel est son initialiseur dsign et pourquoi ce nouvel initialiseur ne lappelle-t-il pas ? Il sagit dune trs bonne question : initWithCoder: reprsente lexception aux rgles dinitialisation. Nous avons prsent implment les mthodes du protocole NSCoding. Pour dclarer que la classe Person implmente le protocole NSCoding, nous devons modier le chier Person.h. Changez la dclaration de la classe de la manire suivante :
@interface Person: NSObject <NSCoding> {

Compilez ensuite le projet. Corrigez les erreurs qui pourraient se prsenter. Si vous le souhaitez, vous pouvez excuter lapplication. Cependant, mme si nous avons expliqu aux objets comment sarchiver, nous ne leur avons pas encore demand de le faire. Vous ne verrez donc aucun changement dans le comportement de lapplication.

Larchitecture de document
Les applications qui manipulent de multiples documents ont de nombreux points communs. Elles crent toutes de nouveaux documents, ouvrent des documents existants, enregistrent ou impriment des documents ouverts et rappellent lutilisateur denregistrer des documents modis lorsquil ferme une fentre ou quitte lapplication. Apple fournit trois classes, NSDocumentController, NSDocument et NSWindowController, qui soccupent dune grande partie des dtails notre place. Ces trois classes composent larchitecture de document. Larchitecture de document est associe au motif de conception Modle-Vue-Contrleur prsent au Chapitre 8. Dans RaiseMan, notre sous-classe de NSDocument, avec laide de NSArrayController, jouera le rle de contrleur, aura un pointeur vers les objets du modle et sera responsable des fonctions suivantes :
m m m m

enregistrer les donnes du modle dans un chier ; charger des donnes du modle partir dun chier ; afcher les donnes du modle dans les vues ; prendre les actions de lutilisateur partir des vues et mettre jour le modle.

158

Cocoa

Info.plist et NSDocumentController Lorsquil compile une application, Xcode inclut un chier nomm Info.plist ; nous modierons plus loin ce chier. Lorsque lapplication est lance, elle lit le chier Info.plist an de connatre le type des chiers reconnus. Si elle dtermine quelle fonctionne comme une application base de documents, elle cre une instance de NSDocumentController (voir Figure 10.2).
Figure 10.2
Contrleur de document.

Nous avons rarement traiter avec le contrleur de document ; il se tient en arrire-plan et soccupe de nombreux dtails notre place. Par exemple, lorsque nous cliquons sur les entres de menu Nouveau (New) ou Tout enregistrer (Save All), le contrleur de document traite la requte. Pour envoyer des messages au contrleur de document, voici comment procder :
NSDocumentController *dc; dc = [NSDocumentController sharedDocumentController];

Le contrleur de document possde un tableau dobjets de document, un pour chaque document ouvert. NSDocument Les objets de document sont des instances dune sous-classe de NSDocument. Par exemple, dans notre application RaiseMan, les objets de document sont des instances de MyDocument. Pour de nombreuses applications, il suft simplement dtendre NSDocument ; il est inutile de soccuper de NSDocumentController ou de NSWindowController.

Chapitre 10

Archivage

159

Enregistrer Les entres de menu Enregistrer (Save), Enregistrer sous... (Save As), Tout enregistrer (Save All) et Fermer (Close) sont toutes diffrentes, mais elles cachent toutes un mme problme : placer le modle dans un chier ou une enveloppe de chier. Une enveloppe de chier est un rpertoire qui, du point de vue de lutilisateur, ressemble un chier. Pour prendre en charge ces entres de menu, la sous-classe de NSDocument doit implmenter lune des trois mthodes suivantes :
- (NSData *)dataOfType:(NSString *)aType error:(NSError *)e

Lobjet de document fournit le modle placer dans le chier sous forme dun objet NSData. NSData est principalement un tampon doctets. Cette manire dimplmenter lenregistrement dans une application base de documents est la plus simple et la plus rpandue. La mthode retourne nil si la cration de lobjet de donnes est impossible, et lutilisateur recevra un message dalerte signalant lchec de la tentative denregistrement. Le type est galement pass en argument, ce qui permet denregistrer le document dans lun des diffrents formats possibles. Par exemple, si nous crivons un programme graphique, nous pouvons permettre lutilisateur denregistrer limage dans un chier gif ou jpg. Lors de la cration de lobjet de donnes, aType prcise le format denregistrement demand par lutilisateur. Si nous ne grons quun seul type, nous pouvons simplement ignorer ce paramtre. Pour indiquer que lenregistrement des donnes est impossible, nous retournons nil et crons un objet NSError qui dcrit le problme.
- (NSFileWrapper *)fileWrapperOfType:(NSString *)aType error:(NSError *)e

Lobjet de document retourne le modle sous forme dun objet NSFileWrapper. Il sera crit dans le systme de chiers, lemplacement indiqu par lutilisateur.
- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError

Lobjet de document reoit lURL et le type. Il est responsable de lenregistrement des donnes dans lURL. En gnral, lURL est un chier sur le systme de chiers. Cette mthode retourne YES si lenregistrement est russi, NO en cas dchec. Si nous retournons NO, nous devons galement crer un objet NSError qui dcrit le problme.
NSError peut amener une certaine confusion. Voici lide sous-jacente : si la mthode est incapable deffectuer son travail, elle cre un objet NSError et place un pointeur sur

160

Cocoa

cette erreur dans ladresse fournie. Par exemple, si nous voulons lire un objet NSData depuis un chier, nous passons ladresse o un pointeur sur lerreur devra tre stock :
NSError *e; NSData *d = [NSData dataWithContentsOfFile:@"/tmp/x.txt" options:0 error:&error]; // La lecture a-t-elle chou? if (d == nil) { NSLog(@"Erreur de lecture: %@", [error localizedDescription]; }

Ainsi, NSData retournera un objet de donnes ou crera un objet derreur. Dans les mthodes denregistrement et de chargement, nous aurons en charge la cration dun objet NSError en cas dchec. Charger Les entres de menu Ouvrir... (Open...), Ouvrir llment rcent (Open Recent) et Revenir la version enregistre (Revert To Saved), bien que toutes diffrentes, cachent le mme problme de base : obtenir le modle partir dun chier ou dune enveloppe de chier. Pour prendre en charge ces entres, la sous-classe de NSDocument doit implmenter lune des trois mthodes suivantes :
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError

Le document reoit un objet NSData dans lequel se trouve le contenu du chier que lutilisateur tente douvrir. La mthode retourne YES si elle a russi crer un modle partir des donnes. Si elle retourne NO, lutilisateur verra un message dalerte qui explique pourquoi la conversion du chier a chou. Le contenu du panneau dalerte est dtermin par lobjet NSError indiqu.
- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError **)outError

Le document lit les donnes depuis un objet NSFileWrapper.


- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError

Lobjet de document reoit une URL, en gnral un chemin vers un chier du systme de chiers. Le document lit les donnes partir du chier. Aprs avoir implment une mthode denregistrement et une mthode de chargement, le document sait comment lire et crire des chiers. Lors de louverture dun chier, il

Chapitre 10

Archivage

161

lit le chier de document avant dexaminer le chier nib. Par consquent, il est impossible denvoyer des messages aux objets de linterface utilisateur juste aprs le chargement de chiers (puisquils nexistent pas encore). Pour rsoudre ce problme, lobjet de document reoit la mthode suivante aprs la lecture du chier nib :
- (void)windowControllerDidLoadNib:(NSWindowController *)x

Dans la sous-classe de NSDocument, nous implmenterons cette mthode an dactualiser les objets de linterface utilisateur. Si lutilisateur choisit Revenir la version enregistre dans le menu, le modle est charg, mais windowControllerDidLoadNib: nest pas invoque. Dans ce cas, lactualisation des objets de linterface utilisateur doit se faire dans la mthode qui charge les donnes, juste pour le cas o lopration aurait t un retour la version enregistre. Pour tenir compte de cette possibilit, une solution classique consiste examiner lun des ensembles doutlets dans le chier nib. Sil vaut nil, cela signie que le chier nib na pas t charg et quil est inutile dactualiser linterface utilisateur. NSWindowController La dernire classe de larchitecture de document est NSWindowController, mais elle ne nous sera pas utile dans limmdiat. Chaque fentre ouverte par un document cre une instance de NSWindowController. Puisque la plupart des applications nont quune seule fentre par document, le comportement par dfaut du contrleur de fentre est parfaitement adapt. Nanmoins, voici quelques situations dans lesquelles nous pourrions avoir crer notre sous-classe de NSWindowController :
m

Nous devons avoir plusieurs fentres sur le mme document. Par exemple, dans un programme de CAO, une fentre de texte pourrait dcrire le solide et une autre fentre, afcher un rendu du solide. Nous voulons placer la logique du contrleur de linterface utilisateur et la logique du contrleur du modle dans des classes spares. Nous voulons crer une fentre sans objet NSDocument correspondant (ce sera le cas au Chapitre 12).

Enregistrer et NSKeyedArchiver
Notre objet sait comment sencoder et se dcoder. Nous allons prsent lutiliser pour ajouter les fonctions denregistrement et de chargement notre application. Lorsque les personnes doivent tre enregistres dans un chier, il sera demand la classe MyDocument de crer une instance de NSData. Une fois cet objet cr et retourn, il sera crit automatiquement dans un chier.

162

Cocoa

Pour crer un objet NSData, nous allons utiliser la classe NSKeyedArchiver. Elle offre la mthode de classe suivante :
+ (NSData *)archivedDataWithRootObject:(id)rootObject

Cette mthode archive les objets dans le tampon doctets de lobjet NSData. Une fois encore, nous revenons lide "je lai dit deux amis, qui lont dit deux amis". Lorsque nous encodons un objet, il encode ses objets, qui encodent leurs objets, etc. Dans notre cas, nous allons encoder le tableau employees. Il encodera les objets Person dont il dtient des rfrences. Puisque nous avons implment encodeWithCoder:, chaque objet Person encodera son tour la chane personName et la valeur en virgule ottante expectedRaise. Pour ajouter les possibilits denregistrement notre application, nous modions la mthode dataOfType:error: de MyDocument.m :
- (NSData *)dataOfType:(NSString *)aType error:(NSError **)outError { // Fin de modification. [[tableView window] endEditingFor:nil]; // Crer un objet NSData partir du tableau des employs. return [NSKeyedArchiver archivedDataWithRootObject:employees]; }

Vous remarquerez que largument derreur est ignor. Il ny aura pas derreurs.

Charger et NSKeyedUnarchiver
Nous ajoutons prsent le chargement des chiers. Une fois encore, NSDocument soccupe dune grande partie des dtails pour nous. Pour le dsarchivage, nous utiliserons NSKeyedUnarchiver, qui offre une mthode trs pratique :
+ (id)unarchiveObjectWithData:(NSData *)data

Dans la classe MyDocument, nous modions la mthode readFromData:ofType:error: de la manire suivante :


- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError { NSLog(@"Lecture des donnes de type %@", typeName); NSMutableArray *newArray = nil; @try { newArray = [NSKeyedUnarchiver unarchiveObjectWithData:data]; } @catch (NSException *e) {

Chapitre 10

Archivage

163

if (outError) { NSDictionary *d = [NSDictionary dictionaryWithObject:@"Les donnes sont corrompues." forKey:NSLocalizedFailureReasonErrorKey]; *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:d]; } return NO; } [self setEmployees:newArray]; return YES; }

Nous pouvons actualiser linterface utilisateur aprs le chargement du chier nib, mais NSArrayController sen chargera notre place. La mthode windowControllerDidLoadNib: na rien faire. Nous la laissons de ct pour le moment, pour y revenir au Chapitre 13 :
- (void)windowControllerDidLoadNib:(NSWindowController *)aController { [super windowControllerDidLoadNib:aController]; }

Vous noterez que le chier nib charger est demand au document lorsque celui-ci est ouvert ou cr. Cette mthode ne ncessite aucune modication :
- (NSString *)windowNibName { return @"MyDocument"; }

La fentre est marque automatiquement comme modie ds quun changement est effectu car nous avons correctement activ le mcanisme dannulation. Lorsque nous signalons les modications du document son gestionnaire dannulation, celui-ci marque automatiquement le document comme modi. ce stade, lapplication peut lire et crire des chiers. Compilez lapplication et testezla. Tout devrait fonctionner correctement, mais les chiers auront lextension .????. Nous devons assigner une extension notre application dans le chier Info.plist.

Affecter une extension et une icne au type de chier


Nous aimerions que les chiers de RaiseMan aient lextension .rsmn, ainsi quune icne. Tout dabord, trouvez un chier .icns (par exemple /Developer/Examples/ AppKit/CompositeLab/BBall.icns) et copiez-le dans le projet. Faites-la glisser depuis le Finder sur la vue Groups and Files de Xcode. Dposez-la dans le groupe Resources (voir Figure 10.3).

164

Cocoa

Figure 10.3
Dposer une icne dans un projet.

Xcode afche une feuille dans laquelle vous devez cocher Copy items into destination groups folder (voir Figure 10.4). Le chier de licne sera ainsi copi dans le rpertoire du projet.
Figure 10.4
En faire une copie.

Chapitre 10

Archivage

165

Pour prciser linformation de type du document, slectionnez la cible RaiseMan dans Xcode et choisissez Get Info dans le menu File. Sous longlet Properties, xez lidentiant de lapplication com.bignerdranch.RaiseMan, et Icon File BBall. Dans la liste des types de documents, xez le nom RaiseMan Doc, les extensions rsmn, et licne du type de chier BBall. Toute cette procdure est rsume la Figure 10.5. Les colonnes ont t rorganises an de prsenter toutes les informations importantes.

Figure 10.5
Prciser licne et les types de document.

Compilez et excutez lapplication. Vous devez pouvoir enregistrer les donnes dans un chier et les relire. Dans Finder, licne BBall.icns sera utilise pour les chiers .rsmn. Une application est un rpertoire qui contient les chiers nib, les images, les sons et le code excutable de lapplication. Dans Terminal, excutez les commandes suivantes :
> cd /Applications/TextEdit.app/Contents > ls

166

Cocoa

Vous verrez trois choses intressantes : 1. Le chier Info.plist, qui comprend les informations concernant lapplication, ses types de chiers et ses icnes associes. Le Finder utilise ces informations. 2. Le rpertoire MacOS/, qui contient le code excutable. 3. Le rpertoire Resources/, dans lequel se trouvent les images, les sons et les chiers nib de lapplication. Vous y trouverez des ressources localises pour diffrentes langues.

Pour les plus curieux : empcher les boucles innies


Le lecteur attentif pourrait sinterroger sur le point suivant : si lobjet A provoque lencodage de lobjet B, et si lobjet B provoque lencodage de lobjet C, et si lobjet C provoque lencodage de lobjet A, larchivage nentre-t-il pas dans une boucle innie ? Ce serait effectivement le cas si la classe NSKeyedArchiver navait pas t conue en tenant compte de cette possibilit. Lorsquun objet est encod, un jeton unique est galement plac dans le ux. Lorsquun objet est archiv, il est ajout la table des objets encods sous ce jeton. Lorsque le mme objet doit tre nouveau encod, NSKeyedArchiver place uniquement un jeton dans le ux. Lors du dcodage dun objet partir du ux, NSKeyedUnarchiver place la fois lobjet et son jeton dans une table. Sil trouve un jeton sans donnes associes, il sait quil doit prendre lobjet dans la table au lieu de crer une nouvelle instance. Cette ide mne la mthode de NSCoder qui perturbe souvent les dveloppeurs lorsquils lisent la documentation :
- (void)encodeConditionalObject:(id)anObject forKey:(NSString *)aKey

Cette mthode est employe lorsquun objet A possde un pointeur sur un objet B, mais que lobjet A ne se proccupe pas rellement de savoir si B est archiv. Cependant, si un autre objet a archiv B, A voudrait que le jeton de B soit plac dans le ux. Si aucun autre objet na archiv B, il sera trait comme nil. Par exemple, si nous crivions une mthode encodeWithCoder: pour un objet Moteur (voir Figure 10.6), il pourrait avoir une variable dinstance nomme voiture qui serait un pointeur sur lobjet Voiture dont il fait partie. Si nous archivions simplement le Moteur, nous ne voudrions pas que lintgralit de la Voiture soit archive. Mais, si nous archivions la Voiture, nous voudrions que le pointeur voiture soit correctement affect. Dans ce cas, nous ferions en sorte que lobjet Moteur encode le pointeur voiture de manire conditionnelle.

Chapitre 10

Archivage

167

Figure 10.6
Exemple dencodage conditionnel.

Pour les plus curieux : crer un protocole


Il est trs facile de crer son propre protocole. En voici un exemple, avec deux mthodes, que lon placerait gnralement dans un chier nomm Toto.h :
@protocol Toto - (void)fido:(int)x; - (float)rex; @end

Objective-C 2.0 a ajout @optional la grammaire dun protocole. Nous pouvons dsormais indiquer les mthodes qui sont obligatoires et celles qui sont facultatives :
@protocol Toto - (void)fido:(int)x; - (float)rex; @optional - (int)medor; - (void)filou:(int)x; @end

Dans cet exemple, fido: et rex sont obligatoires, tandis que medor et filou: sont facultatives. Si une classe doit implmenter le protocole Toto et le protocole NSCoding, voici comment la dclarer :
#import "Tata.h" #import "Toto.h" @interface Titi:Tata <Toto, NSCoding> ...etc. @end

Une classe na pas redclarer les mthodes dont elle hrite de sa superclasse, pas plus quelle ne doit redclarer les mthodes des protocoles quelle implmente. Par consquent, dans notre exemple, le chier dinterface de la classe Titi nest pas oblig de lister toutes les mthodes de Tata, Toto et NSCoding.

168

Cocoa

Pour les plus curieux : applications bases sur des documents sans annulation
Le NSUndoManager de notre application sait lorsque des modications nont pas t enregistres. Par ailleurs, la fentre est automatiquement marque comme modie. Que se passe-t-il si nous crivons une application sans signaler les modications au gestionnaire dannulation ?
NSDocument garde une trace du nombre de modications apportes. Il dispose pour cela de la mthode suivante :
- (void)updateChangeCount:(NSDocumentChangeType)change

Le paramtre NSDocumentChangeType peut prendre les valeurs NSChangeDone, NSChangeUndone ou NSChangeCleared. NSChangeDone incrmente le compteur de modications, NSChangeUndone dcrmente le compteur et NSChangeCleared linitialise zro. La fentre est marque modie tant que le compteur des modications est diffrent de zro.

Identiants de type universel


Lun des problmes rcurrents en informatique se cache derrire la question : "Que reprsentent ces donnes ?" Sur le Mac, cette question se pose en plusieurs endroits : lorsquun chier est ouvert depuis le Finder, lorsque des donnes sont copies depuis le presse-papiers et lorsquun chier est index par Spotlight ou afch avec Coup dil. Jusqu prsent, plusieurs rponses sont mises en uvre : extension de chiers, codes de crateur et types MIME. Apple a dcid que la solution long terme ce problme rsidait dans les identiants de type universel (UTI, universal type identier). Un UTI est une chane qui identie le type dun chier. Les UTI sont organiss de manire hirarchique. Dans le chier Info.plist de notre application, nous pouvons dnir les UTI quelle peut lire et crire, y compris de nouveaux UTI personnels. Info.plist est un chier XML qui contient un ensemble de couples cl-valeur. Pour exporter de nouveaux UTI, nous devons ajouter une nouvelle cl : UTExportedTypeDeclarations. Par exemple, si nous souhaitons crer un UTI pour nos documents RaiseMan, voici ce que nous devons ajouter dans Info.plist :
<key>UTExportedTypeDeclarations</key> <array> <dict> <key>UTTypeIdentifier</key> <string>com.bignerdranch.raiseman-doc</string> <key>UTTypeDescription</key> <string>RaiseMan Document</string>

Chapitre 10

Archivage

169

<key>UTTypeConformsTo</key> <array> <string>public.data</string> </array> <key>UTTypeTagSpecification</key> <dict> <key>com.apple.ostype</key> <string>rsmn</string> <key>public.filename-extension</key> <array> <string>rsmn</string> </array> </dict> </dict> </array>

Nous pouvons ensuite utiliser cet UTI dans linspecteur des proprits (voir Figure 10.7).
Figure 10.7
Fixer lUTI.

La liste de tous les UTI dnis par le systme est disponible dans la documentation dApple.

11
Bases de Core Data
Au sommaire de ce chapitre
NSManagedObjectModel Interface Fonctionnement de Core Data

Lapplication implmente jusqu prsent gre un tableau dobjets, prend en charge les annulations et assure lenregistrement et le chargement depuis un chier. Vous limaginez sans peine, les applications de ce type sont trs nombreuses. Apple a donc dcid den faciliter lcriture : m NSArrayController soccupera du tableau des objets. m Les liaisons limineront une grande partie du code ncessaire synchroniser les objets du modle et les vues. m NSManagedObjectContext observera les variables dinstance des objets de donnes, prendra en charge les annulations et soccupera du chargement et de lenregistrement des donnes. En rsum, grce Core Data et aux liaisons, lapplication RaiseMan que nous avons crite peut tre cre sans aucun code. Dans cette section, nous allons crire une application Core Data (pas diffrente de RaiseMan) sans code.

NSManagedObjectModel
Pour savoir comment enregistrer et charger les donnes de nos objets, le systme doit disposer dinformations sur ces donnes. Quels sont les noms des attributs de lobjet ? Quels sont leurs types ? Pour fournir ces informations, nous allons crer un modle. Xcode propose un diteur qui facilite la description des objets de donnes. Au moment de lexcution, ce chier sera lu et une instance de NSManagedObjectModel sera cre.

172

Cocoa

Le modle utilise une terminologie quelque peu inhabituelle. la place de classe, le modle parle dentit. la place de variable dinstance, il emploie le mot proprit. Dans le modle, il existe deux sortes de proprits : les attributs et les relations. Un attribut contient un type de donnes simple, comme une chane de caractres, une date ou un nombre. Nous reviendrons plus loin sur les relations. Lapplication RaiseMan sest appuye sur une sous-classe de NSDocument nomme MyDocument. Dans lapplication de ce chapitre, nous utiliserons une sous-classe de NSPersistentDocument nomme MyDocument. NSPersistentDocument lit automatiquement le modle et cre un NSManagedObjectContext. Elle va nous viter dcrire de nombreuses lignes de code. Dmarrez Xcode et crez un nouveau projet de type Core Data Document-based Application. Nommez-le CarLot. Imaginons que nous possdions plusieurs voitures doccasion. Lapplication va nous permettre deffectuer le suivi des voitures que nous souhaitons vendre. Une fois termine, elle ressemblera celle illustre la Figure 11.1.
Figure 11.1
Lapplication termine.

Dans le nouveau projet, sous Models, ouvrez MyDocument.xcdatamodel. En bas de la vue Entity, cliquez sur le bouton + pour crer une nouvelle entit. Nommez-la Car.

Chapitre 11

Bases de Core Data

173

Lentit Car tant slectionne, choisissez Add Attribute dans le menu afch par le bouton + en bas de la vue Properties. Ajoutez six attributs et donnez-leur les noms et les types suivants :
Nom
condition datePurchased makeModel onSpecial photo price

Type Int 16 Date String Boolean Binary data Decimal

La Figure 11.2 illustre le rsultat dans loutil de modlisation.


Figure 11.2
Le modle termin.

Nous pourrions videmment ajouter bien dautres choses dans le modle, mais cela suft pour cet exercice.

174

Cocoa

Interface
Ouvrez MyDocument.nib. Dans la fentre, supprimez le champ de texte Your document contents here. Faites glisser un contrleur de tableau dans la fentre du chier nib. Il sera utilis par le NSManagedObjectContext de lobjet document pour obtenir et enregistrer des donnes. Servez-vous de linspecteur Bindings pour lier le managedObjectContext du contrleur de tableau au managedObjectContext du Files Owner (voir Figure 11.3).
Figure 11.3
Donner au contrleur de tableau un contexte dobjet gr.

Dans linspecteur Attributes, congurez le contrleur de tableau an quil prenne ses donnes depuis lentit Car (voir Figure 11.4). Activez galement loption Prepares Content pour que le contrleur de tableau les rcupre immdiatement aprs sa cration. Ltiquette sous les objets dans la fentre du chier nib na pas dimportance. Jai dcid de donner lintitul Cars au contrleur de tableau. Lorsque le chier nib contient plusieurs contrleurs de tableau, les tiquettes permettent dliminer toute confusion. Crer et congurer des vues Faites glisser une vue tableau (depuis Cocoa > Views & Cells > Data Views). laide de linspecteur Attributes, donnez-lui trois colonnes intitules Marque/ Modle, Prix et Spcial. Dposez un formateur numrique (depuis Cocoa > Views & Cells > Formatters) sur la colonne Prix. Slectionnez le formateur (reprsent par un petit cercle dans la colonne) et congurez-le pour quil afche une valeur montaire.

Chapitre 11

Bases de Core Data

175

Figure 11.4
Inspecter les attributs du contrleur de tableau Cars.

Mode Entity

Choisissez le formateur 10.4+ et xez le style Currency. Cochez galement les cases Generate Decimal Numbers et Always Shows Decimal (voir Figure 11.5).
Figure 11.5
Congurer un formateur.

Puisque la troisime colonne contiendra des cases cocher, faites glisser une cellule de type case cocher (depuis Cocoa > Views & Cells > Cells) sur cette colonne. Slectionnez la cellule et effacez son intitul (voir Figure 11.6).

176

Cocoa

Figure 11.6
Dposer une cellule de type case cocher.

Sous la vue tableau, dposez un NSDatePicker, deux boutons, un NSImageView (appel Image Well dans la bibliothque) et un NSLevelIndicator. Placez des tiquettes de texte ct du calendrier et de lindicateur de niveau. Les boutons doivent afcher les intituls Nouveau et Supprimer. Les tiquettes doivent contenir le texte Date dachat: et tat:. Dans linspecteur Attributes du NSLevelIndicator, xez ses valeurs min 0 et max 5. Donnez-lui le style Rating (pour avoir les toiles). Par ailleurs, rendez lindicateur de niveau modiable. Le rsultat est illustr la Figure 11.7.
Figure 11.7
Attributs de NSLevelIndicator.

Chapitre 11

Bases de Core Data

177

laide de linspecteur Attributes, rendez le NSImageView modiable. Slectionnez le calendrier, la vue image, les deux tiquettes et lindicateur de niveau. Enveloppez-les dans une bote en choisissant lentre de menu Layout > Embed Objects In > Box (voir Figure 11.8).
Figure 11.8
Inclure des objets dans une bote.

Connexions et liaisons Nous allons prsent tablir de nombreuses liaisons. Nous les examinons une une ; la Figure 11.9 illustre les liaisons entre les vues et le contrleur de tableau.

Figure 11.9
Rcapitulatif des liaisons.

178

Cocoa

Rappel : dans cet ouvrage, nous ntablirons aucune liaison avec une vue dlement, une vue tableau ou une cellule. En revanche, nous allons crer des liaisons avec les colonnes dune vue tableau, qui contiennent des cellules et se trouvent lintrieur des vues tableau, qui, leur tour, se trouvent dans des vues dlement. Liez le champ value de chaque colonne (Cars est le NSArrayController) :
Liaison
value de la colonne 0 value de la colonne 1 value de la colonne 2

Vers Cars Cars Cars

Cl du contrleur
arrangedObjects arrangedObjects arrangedObjects

Chemin de cl
makeModel price onSpecial

Faites en sorte que le bouton Nouveau dclenche la mthode add: du contrleur de tableau (voir Figure 11.10).

Figure 11.10
Fixer la cible/action du bouton Nouveau.

Associez linvocation de la mthode remove: du contrleur de tableau au bouton Supprimer.

Chapitre 11

Bases de Core Data

179

Liez les valeurs des contrles la slection du contrleur de tableau :


Liaison
value du calendrier enabled du bouton Supprimer value de lindicateur de niveau

Vers Cars Cars Cars

Cl du contrleur
selection canRemove selection

Chemin de cl
datePurchased

condition

Liez les donnes, non la valeur, de la vue image Cars. Choisissez la cl du contrleur selection et le chemin de cl photo. Cochez galement la case intitule Conditionally Sets Editable (voir Figure 11.11).
Figure 11.11
Liaison de la vue image.

Liez prsent Title With Pattern de la bote Cars. Fixez la cl du contrleur selection, le chemin de cl du modle makeModel, le motif dafchage Dtails pour %{title1}@, No Selection Placeholder <Pas de slection>, et Null Placeholder <Pas de Marque/Modle>. Le rsultat de ces oprations est illustr la Figure 11.12. Le texte des deux premires colonnes ne doit apparatre en gras que si la voiture est spciale. Liez Font Bold la donne onSpecial darrangedObjects de Cars (voir Figure 11.13).

180

Cocoa

Figure 11.12
Liaison de la bote.

Figure 11.13
Les voitures spciales apparaissent en gras.

Cest termin. Compilez et excutez lapplication. Lenregistrement et le chargement doivent fonctionner. Lannulation doit galement fonctionner. Magique, non ?

Chapitre 11

Bases de Core Data

181

Fonctionnement de Core Data


Mme si nous navons pas crit une seule ligne de code, de nombreux objets ont t crs pour que lapplication fonctionne. La Figure 11.14 en prsente quelques-uns.

Figure 11.14
Vue densemble de Core Data.

Le NSPersistentDocument lit le modle cr et lutilise pour gnrer des instances de NSManagedObjectModel. Dans notre cas, le modle dobjet gr possde un NSEntityDescription qui dcrit notre entit Car. Cette description dentit possde plusieurs instances de NSAttributeDescription. Aprs avoir obtenu le modle, le document persistant cre une instance de NSPersistentStoreCoordinator et une instance de NSManagedObjectContext. Cette dernire obtient des instances de NSManagedObject partir du systme de stockage des objets. Pendant que ces objets grs se trouvent en mmoire, le contexte dobjet gr les observe. Ds que les donnes contenues dans les objets grs sont modies, le contexte dobjet gr enregistre laction dannulation correspondante dans le NSUndoManager du document. Il sait galement quels objets ont t modis et doivent tre enregistrs. Par consquent, parmi les classes du framework Core Data, nous allons trs souvent interagir avec NSManagedObjectContext. La rcupration des objets se fera avec NSManagedObjectContext. Lenregistrement des modications apportes au graphe dobjet se fera avec NSManagedObjectContext.

182

Cocoa

Puisque lajout des voitures au systme se fera probablement le jour de leur achat, il serait intressant que lattribut datePurchased soit x la date du jour. Une bonne manire de procder consiste crer une sous-classe de NSArrayController et rednir sa mthode newObject. Dans Xcode, crez un nouveau chier de type Objective-C Class. Nommez la classe CarArrayController. Dans CarArrayController.h, faites-en une sous-classe de NSArrayController :
#import <Cocoa/Cocoa.h> @interface CarArrayController: NSArrayController {} @end

Dans CarArrayController.m, rednissez newObject :


- (id)newObject { id newObj = [super newObject]; NSDate *now = [NSDate date]; [newObj setValue:now forKey:@"datePurchased"]; return newObj; }

Dans Interface Builder, slectionnez le contrleur de tableau puis, dans linspecteur Identity, xez sa classe CarArrayController (voir Figure 11.15).
Figure 11.15
Changer la classe dun contrleur de tableau.

Compilez et excutez lapplication. Lorsque de nouvelles voitures sont ajoutes, leur attribut datePurchased doit tre initialis la date du jour.

12
Fichiers nib et NSWindowController
Au sommaire de ce chapitre
NSPanel Ajouter un panneau lapplication Pour les plus curieux : NSBundle

Dans RaiseMan, nous utilisons dj deux chiers nib : MainMenu.nib et MyDocument.nib. MainMenu.nib est charg automatiquement par NSApplication au dmarrage de lapplication. MyDocument.nib est charg automatiquement chaque cration dune instance de MyDocument. Dans cette section, nous allons voir comment charger des chiers nib en utilisant NSWindowController. Pourquoi vouloir charger un chier nib ? En gnral, une application possdera plusieurs fentres, comme une bote de dialogue Rechercher et un panneau Prfrences, qui ne seront utilises quoccasionnellement. En retardant le chargement du chier nib jusquau moment o la fentre est requise, lapplication se lancera plus rapidement. Par ailleurs, si lutilisateur na jamais besoin de la fentre, le programme utilisera moins de mmoire.

184

Cocoa

NSPanel
Dans ce chapitre, nous allons crer un panneau Prfrences. Il sera une instance de NSPanel, qui est une sous-classe de NSWindow. Un panneau est trs peu diffrent dune fentre gnrale, mais tant secondaire, loppos dune fentre de document, son comportement diffre :
m

Un panneau peut devenir la fentre active, mais sans reprsenter la fentre principale. Par exemple, lors de lafchage dune bote dimpression, lutilisateur peut y saisir des informations (il est actif), mais le document visualis par lutilisateur reste la fentre principale (celle qui sera imprime). NSApplication dispose dune outlet mainWindow et dune outlet keyWindow. Elles pointent toutes deux sur la mme fentre, except lorsquun panneau est ouvert ; en gnral, un panneau ne devient pas la fentre principale. Sil possde un bouton de fermeture, il peut tre ferm en appuyant sur la touche chap. Les panneaux napparaissent pas dans la liste afche par le menu Fentre. En effet, lutilisateur qui regarde une fentre sintresse probablement au document, non un panneau.

Toutes les fentres dclarent une variable boolenne nomme hidesOnDeactivate. Si sa valeur est YES, la fentre se masque delle-mme lorsque lapplication est inactive. La plupart des fentres de document xent cette variable NO ; la plupart des panneaux secondaires la xent YES. Grce ce mcanisme, lcran est moins encombr. La valeur de hidesOnDeactivate peut tre xe depuis linspecteur Attributes dans Interface Builder.

Ajouter un panneau lapplication


Le panneau Prfrences que nous allons ajouter ne fera, pour le moment, rien de plus quapparatre. En revanche, au chapitre suivant, nous nous intresserons aux valeurs par dfaut de lutilisateur et accorderons une fonction au panneau Prfrences. Le panneau Prfrences se trouvera dans son propre chier nib. Nous crerons une sous-classe de NSWindowController nomme PreferenceController. Une instance de PreferenceController servira de contrleur pour le panneau Prfrences. Lorsquon cre un panneau secondaire, il est important de ne pas oublier quil peut tre rutilis dans une autre application. En crant une classe qui sert uniquement de contrleur et un chier nib qui contient uniquement le panneau, il est plus facile de rutiliser celui-ci dans une autre application. Les programmeurs branchs diraient "en rendant lapplication plus modulaire, nous maximisons la rutilisation". La modularit facilite galement

Chapitre 12

Fichiers nib et NSWindowController

185

la rpartition des tches entre plusieurs programmeurs. Un directeur dirait "Rex, tu te charges du panneau Prfrences et tu es le seul pouvoir modier le chier nib et la classe contrleur". Les objets placs sur le panneau Prfrences seront connects au contrleur des prfrences. En particulier, il sera la cible dun tmoin de couleur (color well) et dune case cocher. Le panneau Prfrences apparatra lorsque lutilisateur slectionnera lentre de menu Preferences. La Figure 12.1 illustre lexcution de lapplication.
Figure 12.1
Lapplication termine.

La Figure 12.2 prsente un graphe des objets que nous allons crer et les chiers nib dans lesquels ils rsideront. Ouvrez le projet RaiseMan et crez une nouvelle classe Objective-C nomme AppController. Modiez AppController.h de la manire suivante :
#import <Cocoa/Cocoa.h> @class PreferenceController; @interface AppController: NSObject { PreferenceController *preferenceController; } - (IBAction)showPreferencePanel:(id)sender; @end

Notez la syntaxe Objective-C :


@class PreferenceController;

186

Cocoa

Figure 12.2
Graphe des objets et chiers nib.

Cette ligne indique au compilateur quil existe une classe PreferenceController. Nous pouvons ensuite faire la dclaration suivante sans importer le chier den-tte de PreferenceController :
PreferenceController *preferenceController;

Nous pouvons remplacer


@class PreferenceController;

par
#import "PreferenceController.h"

Cette instruction importe len-tte et le compilateur apprend que Preference-Controller est une classe. Mais, puisque la commande import oblige le compilateur analyser un plus grand nombre de chiers, @class rsulte souvent en des compilations plus rapides. Cependant, nous devons toujours importer le chier den-tte de la classe mre, car le compilateur doit connatre les variables dinstance dclares dans la superclasse. Dans notre cas, NSObject.h est import par <Cocoa/Cocoa.h>. Dnir lentre de menu Ouvrez MainMenu.nib. Dans Interface Builder, faites glisser un NSObject depuis la bibliothque (sous Objects & Controllers). Dans linspecteur Identity, xez sa classe AppController (voir Figure 12.3).

Chapitre 12

Fichiers nib et NSWindowController

187

Figure 12.3
Crer une instance dAppController.

En maintenant la touche Contrle enfonce, faites glisser lentre de menu Preferences vers lAppController. Faites-en la cible et xez laction showPreferencePanel: (voir Figure 12.4).

Figure 12.4
Fixer la cible dune entre de menu.

Fermez le chier nib.

188

Cocoa

AppController.m Nous devons prsent crire le code dAppController. Modiez AppController.m an quil contienne le code suivant :
#import "AppController.h" #import "PreferenceController.h" @implementation AppController - (IBAction)showPreferencePanel:(id)sender { // preferenceController est-il nil? if (!preferenceController) { preferenceController = [[PreferenceController alloc] init]; } NSLog(@"affichage de %@", preferenceController); [preferenceController showWindow:self]; } @end

Ce code cre linstance de PreferenceController une seule fois. Si cette instance existe (nest pas nil), la variable preferenceController lui envoie simplement le message showWindow:. Vous aurez remarqu que nous importons galement PreferenceController.h dans le chier .m qui lutilise. Dans Xcode, choisissez New File dans le menu File et crez une nouvelle Objective-C NSWindowController subclass. Nommez-la PreferenceController (voir Figure 12.5).
Figure 12.5
Crer des chiers pour PreferenceController.

Chapitre 12

Fichiers nib et NSWindowController

189

Modiez PreferenceController.h de la manire suivante :


#import <Cocoa/Cocoa.h> @interface PreferenceController: NSWindowController { IBOutlet NSColorWell *colorWell; IBOutlet NSButton *checkbox; } - (IBAction)changeBackgroundColor:(id)sender; - (IBAction)changeNewEmptyDoc:(id)sender; @end

Preferences.nib Dans Xcode, crez un nouveau chier nib vide nomm Preferences.nib (voir Figure 12.6).

Figure 12.6
Crer un chier nib vide.

Il existe des chiers XIB et des chiers NIB. Ils ont la mme fonction, mais les chiers NIB sont dans un format binaire, tandis que les chiers XIB sont dans un format XML. Les chiers XIB sont convertis en chiers NIB la compilation de lapplication. Dans ce cas, pourquoi utiliser des chiers XIB ? Simplement parce quils sont parfaitement adapts aux systmes de gestion des versions, comme Subversion. Double-cliquez sur Preferences.nib pour louvrir dans Interface Builder. Afchez linspecteur Identity, slectionnez Files Owner et xez sa classe PreferenceController (voir Figure 12.7).

190

Cocoa

Figure 12.7
Files Owner est un PreferenceController.

Files Owner Lorsquun chier nib est charg dans une application qui sexcute depuis un certain temps, les objets dj prsents doivent tablir une connexion avec les objets lus depuis le chier nib. Files Owner fournit cette connexion. Dans un chier nib, Files Owner sert demplacement pour un objet qui existera au moment o le chier nib sera charg. Lobjet qui charge le chier nib servira dobjet propritaire. Le propritaire prend lemplacement reprsent par Files Owner. Dans notre application, le propritaire sera linstance de PreferenceController qui a t cre par lAppController. En gnral, lutilisation de Files Owner amne une certaine confusion. Nous ninstancions pas PreferenceController dans le chier nib, mais informons simplement celui-ci que le propritaire, qui sera fourni lors du chargement du chier nib, est un PreferenceController. Agencer linterface utilisateur Crez un nouveau panneau en faisant glisser un panneau partir de la bibliothque (sous Application > Windows) et en le dposant nimporte o sur lcran (voir Figure 12.8). Rduisez la taille du panneau et dposez un tmoin de couleur et une case cocher. Attribuez-leur les tiquettes prsentes la Figure 12.9. Les cases cocher possdent des tiquettes, mais vous devez ajouter un champ de texte pour le tmoin de couleur. Fixez la cible du tmoin de couleur Files Owner (le PreferenceController) et laction changeBackgroundColor: (voir Figure 12.10).

Chapitre 12

Fichiers nib et NSWindowController

191

Figure 12.8
Crer une instance de NSPanel.

Figure 12.9
Linterface termine.

Figure 12.10
Fixer la cible du tmoin de couleur.

192

Cocoa

Faites galement de PreferenceController la cible de la case cocher, mais pour laction changeNewEmptyDoc:. Faites un Contrle-clic sur Files Owner pour afcher la fentre des connexions. Fixez loutlet colorWell de Files Owner lobjet tmoin de couleur et loutlet checkbox lobjet case cocher (voir Figure 12.11).

Figure 12.11
Fixer les outlets colorWell et checkbox.

Faites un Contrle-clic sur Files Owner pour afcher la fentre des connexions. Connectez loutlet window au panneau (voir Figure 12.12).
Figure 12.12
Fixer loutlet window de Files Owner.

Chapitre 12

Fichiers nib et NSWindowController

193

Ouvrez linspecteur Attributes du panneau. Interdisez le redimensionnement, changez le titre de la fentre en Preferences, et enregistrez le chier nib (voir Figure 12.13).

Figure 12.13
Les attributs de la nouvelle fentre.

PreferenceController.m Dans Xcode, modiez PreferenceController.m de la manire suivante :


#import "PreferenceController.h" @implementation PreferenceController - (id)init { if (![super initWithWindowNibName:@"Preferences"]) return nil; return self; } - (void)windowDidLoad { NSLog(@"Le fichier nib a t charg"); } - (IBAction)changeBackgroundColor:(id)sender { NSColor *color = [colorWell color]; NSLog(@"Couleur modifie: %@", color); }

194

Cocoa

- (IBAction)changeNewEmptyDoc:(id)sender { int state = [checkbox state]; NSLog(@"Case cocher modifie %d", state); } @end

Le nom du chier nib charger est indiqu dans la mthode init. Il sera charg automatiquement au moment o il sera requis. Linstance de PreferenceController remplacera Files Owner dans le chier nib. Aprs le chargement du chier nib, le PreferenceController reoit le message windowDidLoad. Le contrleur dobjet a ainsi lopportunit, comme pour awakeFromNib ou windowControllerDidLoadNib:, dinitialiser les objets de linterface utilisateur qui ont t lus depuis le chier nib. Lorsquil reoit pour la premire fois showWindow:, le NSWindowController charge automatiquement le chier nib et place la fentre au premier plan de lcran. Le chier nib nest charg quune seule fois. Lorsque lutilisateur ferme le panneau des prfrences, il est retir de lcran sans tre dsallou. la prochaine ouverture du panneau des prfrences, il sera simplement remis lcran. Les mthodes changeBackgroundColor: et checkboxChanged: sont pour le moment plutt ennuyeuses elles afchent simplement un message. Au chapitre suivant, nous les changerons pour quelles actualisent la base de donnes des valeurs par dfaut de lutilisateur. Compilez et excutez lapplication. Le nouveau panneau doit apparatre. Lutilisation de la case cocher ou du tmoin de couleur doit produire un message dans la console (voir Figure 12.14).
Figure 12.14
Lapplication termine.

Chapitre 12

Fichiers nib et NSWindowController

195

Lorsque lutilisateur voit pour la premire fois un tmoin de couleur, il peut sembler confus. Sil clique sur le bord du tmoin de couleur, le bord est soulign, le panneau de couleur est afch et le tmoin est en mode "actif".

Pour les plus curieux : NSBundle


Un bundle est un rpertoire de ressources qui peuvent tre utilises par une application. Des images, des sons, du code compil et des chiers nib sont des ressources. Le terme plug-in remplace parfois bundle. La classe NSBundle constitue une manire trs lgante de manipuler les bundles. Une application est un bundle. Dans le Finder, lutilisateur voit une application comme nimporte quel autre chier, mais il sagit en ralit dun rpertoire qui contient des chiers nib, du code compil, ainsi que dautres ressources. Ce rpertoire est appel bundle principal de lapplication. Certaines ressources dun bundle peuvent tre localises. Par exemple, il est possible davoir deux versions de toto.nib : une pour les utilisateurs francophones et lautre pour les utilisateurs anglophones. Le bundle contiendra alors les deux sous-rpertoires English.lproj et French.lproj. La version adquate de toto.nib sera place dans le rpertoire correspondant. Lorsque lapplication demande au bundle de charger toto.nib, il charge automatiquement la version franaise de foo.nib si la langue prfre de lutilisateur est le franais. Nous reviendrons sur la localisation au Chapitre 16. Pour obtenir le bundle principal dune application, nous utilisons le code suivant :
NSBundle *monBundle = [NSBundle mainBundle];

Il sagit du bundle le plus utilis. Pour accder des ressources qui se trouvent dans un autre rpertoire, nous devons indiquer le chemin du bundle correspondant :
NSBundle *bonBundle; bonBundle = [NSBundle bundleWithPath:@"~/.monApp/Bon.bundle"];

Une fois lobjet NSBundle disponible, nous pouvons demander les ressources quil contient :
// Lextension est facultative. NSString *chemin = [bonBundle pathForImageResource:@"Maman"]; NSImage *photoMaman = [[NSImage alloc] initWithContentsOfFile:chemin];

Un bundle peut contenir une bibliothque de code. Si lon demande une classe au bundle, il effectue la liaison de la bibliothque et recherche une classe qui correspond au nom indiqu :
Class nouvelleClasse = [bonBundle classNamed:@"Rover"]; id nouvelleInstance = [[nouvelleClasse alloc] init];

196

Cocoa

Lorsque les noms des classes du bundle sont inconnus, il suft de demander la classe principale :
Class uneClasse = [bonBundle principalClass]; id uneInstance = [[uneClasse alloc] init];

Vous le constatez, NSBundle est trs pratique. Dans cette section, le NSBundle tait responsable, sans que lon sen aperoive, du chargement du chier nib. Voici comment charger un chier nib sans utiliser un NSWindowController :
BOOL succes = [NSBundle loadNibNamed:@"About" owner:unObjet];

Notez quil faut indiquer lobjet qui sera le Files Owner.

Exercice
Crez un chier nib avec un panneau propos. Ajoutez une outlet AppController pour dsigner la nouvelle fentre. Ajoutez galement une mthode showAboutPanel:. Chargez le chier nib en utilisant NSBundle et faites dAppController le Files Owner.

13
Valeurs par dfaut de lutilisateur
Au sommaire de ce chapitre
NSDictionary et NSMutableDictionary NSUserDefaults Fixer lidentiant de lapplication Crer des cls pour les valeurs par dfaut Inscrire les valeurs par dfaut Laisser lutilisateur modier les valeurs par dfaut Utiliser les valeurs par dfaut Pour les plus curieux : NSUserDefaultsController Pour les plus curieux : lire et crire les valeurs par dfaut partir

de la ligne de commande Les applications disposent gnralement de panneaux Prfrences qui permettent lutilisateur de choisir une apparence ou un comportement favori. Les choix de lutilisateur sont enregistrs dans la base de donnes des valeurs par dfaut, qui se trouve dans son dossier de dpart. Seuls les choix qui diffrent des valeurs dnies par dfaut par lapplication sont enregistrs dans la base de donnes. Si vous allez dans ~/Bibliothque/Preferences, vous pouvez consulter votre base de donnes des valeurs par dfaut. Les chiers utilisent un format de liste de proprits ; vous pouvez les examiner avec loutil Property List Editor. La classe NSUserDefaults permet une application de dnir ses valeurs par dfaut, denregistrer les prfrences de lutilisateur et de lire les prfrences prcdemment enregistres. Le tmoin de couleur dpos dans la fentre Preferences au Chapitre 12 servira dnir la couleur darrire-plan de la vue tableau. Lorsquelle crera une nouvelle fentre de document, lapplication lira les prfrences partir de la base de donnes des valeurs

198

Cocoa

par dfaut de lutilisateur. Par consquent, seules les fentres cres aprs la modication seront concernes (voir Figure 13.1).
Figure 13.1
Lapplication termine.

Avez-vous remarqu que, lors de son lancement, lapplication ouvre toujours un document sans titre ? La case cocher Ouvrir automatiquement un nouveau document permettra lutilisateur de choisir si le document sans titre doit apparatre.

NSDictionary et NSMutableDictionary
Avant de manipuler les valeurs par dfaut de lutilisateur, nous devons prsenter les classes NSDictionary (voir Figure 13.2) et NSMutableDictionary. Un dictionnaire est un ensemble de couples cl-valeur. Les cls sont des chanes de caractres, tandis que les valeurs sont des pointeurs vers des objets.
Figure 13.2
Une instance de NSDictionary.

Chapitre 13

Valeurs par dfaut de lutilisateur

199

Une mme chane ne peut tre utilise quune seule fois comme cl dans un mme dictionnaire. Pour connatre la valeur associe une cl, nous utilisons la mthode objectForKey: :
unObjet = [monDictionnaire objectForKey:@"toto"];

Si la cl est absente du dictionnaire, cette mthode retourne nil.


NSMutableDictionary est une sous-classe de NSDictionary. Une instance de NSDictionary est cre avec toutes les cls et les valeurs quelle pourra possder. Nous pouvons consulter lobjet, mais pas le modier. En revanche, NSMutableDictionary nous permet dajouter et de retirer des cls et des valeurs.

NSDictionary Un dictionnaire est mis en uvre comme une table de hachage. Par consquent, la recherche des cls est trs rapide. Voici les mthodes de la classe NSDictionary les plus utilises :
- (NSArray *)allKeys

Retourne un nouveau tableau contenant les cls du dictionnaire.


- (unsigned)count

Retourne le nombre de couples cl-valeur prsents dans le dictionnaire.


- (id)objectForKey:(NSString *)aKey

Retourne la valeur associe la cl aKey ou nil si aucune valeur nest associe aKey.
- (NSEnumerator *)keyEnumerator

Les numrateurs sont galement appels itrateurs ou numrations. Ils permettent de parcourir tous les membres dune collection. La mthode prcdente retourne un numrateur qui parcourt toutes les cls dun dictionnaire. Voici comment lutiliser pour afcher tous les couples cl-valeur prsents dans un dictionnaire :
NSEnumerator *e = [monDict keyEnumerator]; for (NSString *s in e) { NSLog(@"cl = %@, valeur = %@", s, [monDict objectForKey:s]); }

200

Cocoa

- (NSEnumerator *)objectEnumerator

Retourne un numrateur qui parcourt toutes les valeurs du dictionnaire. La classe NSArray offre galement la mthode objectEnumerator, qui retourne un numrateur permettant de parcourir tous les lments du tableau. NSMutableDictionary
+ (id)dictionary

Cre un dictionnaire vide.


- (void)removeObjectForKey:(NSString *)aKey

Retire du dictionnaire aKey, ainsi que lobjet de valeur associ.


- (void)setObject:(id)anObject forKey:(NSString *)aKey

Ajoute une entre au dictionnaire. Cette entre est constitue de la cl, aKey, et de lobjet de valeur associ, anObject. Lobjet de valeur reoit un message de garde avant dtre ajout au dictionnaire. Si aKey existe dj dans le destinataire, lobjet de valeur prcdent associ cette cl reoit un message de libration et anObject prend sa place.

NSUserDefaults
Chaque application possde un ensemble de valeurs par dfaut dnies "en usine". Lorsquun utilisateur modie les valeurs par dfaut, seules les diffrences avec les valeurs dusine sont enregistres dans la base de donnes des valeurs par dfaut de lutilisateur. Par consquent, chaque fois que lapplication dmarre, nous devons lui rappeler ses valeurs dusine. Cette opration se nomme inscrire les valeurs par dfaut. Aprs linscription, nous utilisons lobjet des valeurs par dfaut de lutilisateur pour dterminer le comportement de lapplication souhait par lutilisateur. Cette procdure se nomme lire et utiliser les valeurs par dfaut. Les donnes provenant de la base de donnes des valeurs par dfaut de lutilisateur seront lues automatiquement partir du systme de chiers. Nous crerons galement un panneau Prfrences qui permet lutilisateur de dnir ses valeurs par dfaut. Les modications apportes lobjet des valeurs par dfaut seront crites automatiquement sur le systme de chiers. Cette opration sappelle xer les valeurs par dfaut (voir Figure 13.3).

Chapitre 13

Valeurs par dfaut de lutilisateur

201

Figure 13.3
NSUserDefaults et le systme de chiers.

Voici les mthodes de NSUserDefaults les plus utilises :


+ (NSUserDefaults *)standardUserDefaults

Retourne lobjet des valeurs par dfaut partag.


- (void)registerDefaults:(NSDictionary *)dictionary

Inscrit les valeurs dusine de lapplication.


(void)setBool:(BOOL)value forKey:(NSString *)defaultName (void)setFloat:(float)value forKey:(NSString *)defaultName (void)setInteger:(int)value forKey:(NSString *)defaultName (void)setObject:(id)value forKey:(NSString *)defaultName

Ces mthodes changent et enregistrent les souhaits de lutilisateur.


(BOOL)boolForKey:(NSString *)defaultName (float)floatForKey:(NSString *)defaultName (int)integerForKey:(NSString *)defaultName (id)objectForKey:(NSString *)defaultName

Ces mthodes lisent les valeurs par dfaut. Si lutilisateur ne les a pas modies, les valeurs dusine sont retournes.
- (void)removeObjectForKey:(NSString *)defaultName

Supprime les prfrences de lutilisateur, pour que lapplication revienne ses valeurs dusine.

202

Cocoa

Priorit des diffrentes valeurs par dfaut Jusqu prsent, nous avons mentionn deux niveaux de priorit : ce que lutilisateur crit dans sa base de donnes des valeurs par dfaut est prioritaire sur les valeurs dusine. En ralit, il existe plusieurs autres niveaux de priorit, appels domaines. Voici les domaines utiliss par une application, en commenant par celui de plus haute priorit :
m

Arguments. Ceux passs sur la ligne de commande. En gnral, une application est lance par un double-clic sur son icne, non depuis la ligne de commande. Par consquent, cette caractristique est rarement employe dans une application en production. Application. Ce qui provient de la base de donnes des valeurs par dfaut de lutilisateur. Global. Ce que lutilisateur a dni pour lensemble de son systme. Langue. Ce qui est dni en fonction de la langue favorite de lutilisateur. Valeurs par dfaut inscrites. Les valeurs dusine de lapplication.

m m m

Fixer lidentiant de lapplication


Comment se nomme le chier des proprits cr par notre application dans ~/Bibliothque/Preferences ? Par dfaut, il utilise lidentiant de lapplication qui la cr. Cet identiant, dni au Chapitre 10, est com.bignerdranch.RaiseMan. Le nom du chier est donc com.bignerdranch.RaiseMan.plist.

Crer des cls pour les valeurs par dfaut


Nous allons inscrire, lire et xer des valeurs par dfaut dans plusieurs classes de notre application. Pour tre certains de toujours utiliser le mme nom, nous dclarons ces chanes dans un chier unique que nous importons avec #import dans le chier o nous devrons utiliser les noms. Il existe plusieurs manires de procder. Par exemple, nous pouvons utiliser la directive #define du prprocesseur C, mais les programmeurs Cocoa se servent gnralement de variables globales. Ajoutez les lignes suivantes dans le chier PreferenceController.h, aprs linstruction #import :
extern NSString * const BNRTableBgColorKey; extern NSString * const BNREmptyDocKey;

Chapitre 13

Valeurs par dfaut de lutilisateur

203

Ensuite, dnissez ces variables dans PreferenceController.m. Placez-les aprs les lignes #import et avant @implementation :
NSString * const BNRTableBgColorKey = @"TableBackgroundColor"; NSString * const BNREmptyDocKey = @"EmptyDocumentFlag";

Pourquoi dclarer des variables globales qui ne contiennent que des chanes constantes ? En effet, il suft simplement de mmoriser la chane et de la saisir lorsquelle est requise. Cest vrai, mais nous pourrions mal orthographier la chane. Si elle est place entre des guillemets, le compilateur acceptera la chane errone. En revanche, si le nom dune variable globale est mal orthographi, le compilateur dtectera lerreur. Pour que les variables globales nentrent pas en conit avec les variables globales dune autre entreprise, nous les prxons par BNR (pour Big Nerd Ranch). Les variables globales de Cocoa utilisent le prxe NS. Ces prxes ne sont importants que dans le cas o nous utilisons des classes et des frameworks dvelopps par des tiers. Notez que les noms des classes sont galement globaux. Nous pourrions leur ajouter le prxe BNR pour viter les conits de noms avec les classes dun autre programmeur.

Inscrire les valeurs par dfaut


Le premier message reu par chaque classe est initialize. Pour tre certains que nos valeurs par dfaut sont inscrites le plus tt possible, nous rednissons initialize dans AppController.m :
+ (void)initialize { // Crer un dictionnaire. NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary]; // Archiver lobjet de couleur. NSData *colorAsData = [NSKeyedArchiver archivedDataWithRootObject: [NSColor yellowColor]]; // Placer les valeurs par dfaut dans le dictionnaire. [defaultValues setObject:colorAsData forKey:BNRTableBgColorKey]; [defaultValues setObject:[NSNumber numberWithBool:YES] forKey:BNREmptyDocKey]; // Inscrire le dictionnaire des valeurs par dfaut. [[NSUserDefaults standardUserDefaults]

registerDefaults: defaultValues];
NSLog(@"Valeurs par dfaut inscrites: %@", defaultValues); }

Puisquil sagit dune mthode de classe, sa dclaration dbute par le signe +. Vous remarquerez que nous devons enregistrer les couleurs sous forme dun objet de donnes. Les objets NSColor ne savent pas comment se transformer en liste de proprits. Nous les plaons donc dans un objet de donnes qui sait comment procder.

204

Cocoa

Les classes de liste de proprits NSString, NSArray, NSDictionary, NSCalendarDate, NSData et NSNumber savent se convertir en listes de proprits. Une liste de proprits peut combiner volont ces classes. Par exemple, un dictionnaire contenant des tableaux de dates est une liste de proprits.

Laisser lutilisateur modier les valeurs par dfaut


Nous allons prsent revenir sur la classe PreferenceController pour que le panneau Prfrences actualise la base de donnes des valeurs par dfaut. Dclarez les mthodes suivantes dans PreferenceController.h :
- (NSColor *)tableBgColor; - (BOOL)emptyDoc;

Puis modiez PreferenceController.m :


#import "PreferenceController.h" NSString * const BNRTableBgColorKey = @"TableBackgroundColor"; NSString * const BNREmptyDocKey = @"EmptyDocumentFlag"; @implementation PreferenceController - (id)init { if (![super initWithWindowNibName:@"Preferences"]) return nil; return self; } - (NSColor *)tableBgColor { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSData *colorAsData = [defaults objectForKey:BNRTableBgColorKey]; return [NSKeyedUnarchiver unarchiveObjectWithData:colorAsData]; } - (BOOL)emptyDoc { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; return [defaults boolForKey:BNREmptyDocKey]; } - (void)windowDidLoad { [colorWell setColor:[self tableBgColor]]; [checkbox setState:[self emptyDoc]]; } - (IBAction)changeBackgroundColor:(id)sender { NSColor *color = [colorWell color];

Chapitre 13

Valeurs par dfaut de lutilisateur

205

NSData *colorAsData = [NSKeyedArchiver archivedDataWithRootObject:color]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:colorAsData forKey:BNRTableBgColorKey]; } - (IBAction)changeNewEmptyDoc:(id)sender { int state = [checkbox state]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setBool:state forKey:BNREmptyDocKey]; } @end

La mthode windowDidLoad lit les valeurs par dfaut et rete la conguration actuelle dans le tmoin de couleur et la case cocher. Dans changeBackgroundColor: et changeNewEmptyDoc:, nous actualisons la base de donnes des valeurs par dfaut. Vous devriez prsent pouvoir compiler et excuter lapplication. Elle lira et modiera la base de donnes des valeurs par dfaut. Le panneau Prfrences afchera la dernire couleur choisie et indiquera si la case tait coche ou non. Cependant, nous navons pas encore exploit ces informations. Le document sans titre apparat toujours et larrireplan de la vue tableau reste blanc.

Utiliser les valeurs par dfaut


Nous allons prsent utiliser les valeurs par dfaut. Tout dabord, nous ferons dAppController un dlgu de lobjet NSApplication et supprimerons la cration dun document sans titre, selon les valeurs par dfaut de lutilisateur. Ensuite, dans MyDocument, nous xerons la couleur darrire-plan de la vue tableau partir des valeurs par dfaut de lutilisateur. Supprimer la cration des documents sans titre Comme prcdemment, la cration dun dlgu se fait en deux tapes : implmenter la mthode dlgue et congurer loutlet delegate pour quelle pointe sur lobjet (voir Figure 13.4). Avant de crer automatiquement un nouveau document sans titre, lobjet NSApplication envoie le message applicationShouldOpenUntitledFile: son dlgu. Dans AppController.m, ajoutez la mthode suivante :
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender { NSLog(@"applicationShouldOpenUntitledFile:"); return [[NSUserDefaults standardUserDefaults] boolForKey:BNREmptyDocKey]; }

206

Cocoa

Figure 13.4
Le dlgu supprime la cration des documents sans titre.

Pour quAppController soit le dlgu de lobjet NSApplication, ouvrez le chier MainMenu.nib et faites un Contrle-clic sur Files Owner, qui reprsente lobjet NSApplication, an douvrir sa fentre des connexions. Faites glisser loutlet delegate vers AppController (voir Figure 13.5).
Figure 13.5
Slectionner loutlet delegate.

Fixer la couleur darrire-plan de la vue tableau Aprs que le chier nib dune nouvelle fentre de document a t dsarchiv, lobjet MyDocument reoit le message windowControllerDidLoadNib:. ce moment-l, nous pouvons actualiser la couleur darrire-plan de la vue tableau. La mthode suivante existe dj dans MyDocument.m ; modiez-la de la manire suivante :
- (void)windowControllerDidLoadNib:(NSWindowController *)aController { [super windowControllerDidLoadNib:aController]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSData *colorAsData;

Chapitre 13

Valeurs par dfaut de lutilisateur

207

colorAsData = [defaults objectForKey:BNRTableBgColorKey]; [tableView setBackgroundColor: [NSKeyedUnarchiver unarchiveObjectWithData:colorAsData]]; }

Vriez que le chier PreferenceController.h est import au dbut de MyDocument.m an de pouvoir utiliser les variables globales qui y sont dclares. Compilez et excutez lapplication.

Pour les plus curieux : NSUserDefaultsController


Parfois, une liaison avec une valeur de lobjet NSUserDefaults sera ncessaire. Grce la classe NSUserDefaultsController, cest possible. Tous les chiers nib dune application utilisent une mme instance partage de NSUserDefaultsController. Par exemple, si nous souhaitons utiliser des liaisons, la place dune cible et dune action, pour manipuler la case cocher du panneau Prfrences, nous la lions value.EmptyDocumentFlag du NSUserDefaultsController partag (voir Figure 13.6).
Figure 13.6
Liaison au NSUserDefaultsController.

Pour les plus curieux : lire et crire les valeurs par dfaut partir de la ligne de commande
La base de donnes des valeurs par dfaut de lutilisateur se trouve dans le rpertoire ~/ Bibliothque/Preferences/. Pour la modier partir de la ligne de commande, nous utilisons un outil appel defaults. Par exemple, pour consulter vos valeurs par dfaut pour Xcode, ouvrez une fentre Terminal et saisissez la commande suivante :
defaults read com.apple.Xcode

208

Cocoa

Vous obtenez alors toutes vos valeurs par dfaut pour Xcode. Voici les premires lignes de mes prfrences :
{ DocViewerHasSetPrefs = YES; NSNavBrowserPreferedColumnContentWidth = 155; NSNavLastCurrentDirectoryForOpen = "~/RaiseMan"; NSNavLastRootDirectoryForOpen = "~"; NSNavPanelExpandedSizeForOpenMode = "{518, 400}"; NSNavPanelFileListModeForOpenMode = 1;

Nous pouvons galement modier la base de donnes des valeurs par dfaut. Pour que le rpertoire par dfaut de Xcode dans NSOpenPanel soit /Users, nous utilisons la commande suivante :
defaults write com.apple.Xcode NSNavLastRootDirectoryForOpen /Users

Essayez ceci :
defaults read com.bignerdranch.RaiseMan

Pour obtenir vos valeurs par dfaut globales, saisissez cette commande :
defaults read NSGlobalDomain

Exercice
Ajoutez un bouton au panneau Prfrences pour supprimer toutes les valeurs par dfaut de lutilisateur. Intitulez-le Rinitialiser les prfrences. Noubliez pas dactualiser la fentre des prfrences an de reter les nouvelles valeurs par dfaut.

14
Notications
Au sommaire de ce chapitre
Ce que sont les notications Ce que ne sont pas les notications NSNotication et NSNoticationCenter Poster une notication Inscrire un observateur Traiter la notication Le dictionnaire userInfo Pour les plus curieux : dlgus et notications

Un utilisateur pourrait avoir ouvert plusieurs documents RaiseMan, lorsquil dcide quil est trop difcile de les lire avec un arrire-plan violet. Il utilise le panneau Prfrences, modie la couleur darrire-plan, mais est trs du de constater que les fentres existantes ne changent pas de couleur. Il vous contacte an de signaler ce problme. Vous lui rpondez alors que les valeurs par dfaut sont lues uniquement au moment de la cration de la fentre de document. Il doit simplement enregistrer le document, le fermer et louvrir nouveau. Cela ne lui plat pas et il prfrerait que toutes les fentres existantes soient mises jour. Mais combien sont-elles ? Devez-vous conserver une liste de tous les documents ouverts ?

Ce que sont les notications


La tche est plus simple que cela. Chaque application en cours dexcution possde une instance de NSNotificationCenter qui joue le rle de tableau dafchage. Les objets sinscrivent pour indiquer quils sont intresss par certains avis ("Merci de mcrire si quelquun trouve un chien perdu"). Lobjet inscrit est un observateur.

210

Cocoa

Dautres objets peuvent poster des notications ("Jai trouv un chien perdu"). La notication est transmise tous les objets qui ont signal leur intrt pour cet avis. Lobjet qui poste la notication est un annonceur. Un grand nombre de classes Cocoa standard postent des notications. Par exemple, les fentres signalent quelles ont chang de taille. Lorsque la slection dune vue tableau change, la vue poste une notication. Les notications mises par les objets Cocoa standard sont recenses dans la documentation en ligne. Dans notre exemple, nous inscrirons tous nos objets MyDocument en tant quobservateurs. Le contrleur des prfrences mettra une notication lorsque lutilisateur choisira une nouvelle couleur. En voyant la notication, les objets MyDocument modieront la couleur darrire-plan. Avant que lobjet MyDocument ne soit dsallou, nous devons le retirer de la liste des observateurs. En gnral, cela se fait dans la mthode dealloc.

Ce que ne sont pas les notications


Lorsque les programmeurs entendent parler pour la premire fois du centre de notication, ils imaginent parfois une forme de communication interprocessus. Ils pensent pouvoir crer un observateur dans une application et poster des notications partir dun objet situ dans une autre application. Ce schma ne fonctionne pas : un centre de notication permet aux objets dune application denvoyer des notications dautres objets de la mme application. Les notications ne franchissent pas les limites des applications.

NSNotication et NSNoticationCenter
Les objets de notication sont trs simples. Une notication ressemble une enveloppe dans laquelle lannonceur place les informations destines aux observateurs. Une notication possde deux variables dinstance importantes : name et object. object est presque toujours un pointeur vers lobjet qui met la notication. Il peut tre compar une adresse de retour. La notication offre galement deux mthodes intressantes :
- (NSString *)name - (id)object

Chapitre 14

Notications

211

NSNotificationCenter constitue le cerveau de lopration. Il permet denregistrer des objets observateurs, de poster des notications et de dsinscrire des observateurs.

Voici les mthodes de NSNotificationCenter les plus utilises :


+ (NSNotificationCenter *)defaultCenter

Retourne le centre de notication.


- (void)addObserver:(id)anObserver selector:(SEL)aSelector name:(NSString *)notificationName object:(id)anObject

Inscrit anObserver pour quil reoive les notications de nom notificationName et contenant anObject (voir Figure 14.1). Lorsquune notication nomme notificationName et contenant lobjet anObject est poste, anObserver reoit un message aSelector avec la notication en argument.

Figure 14.1
Sinscrire pour recevoir des notications.

212

Cocoa

Si notificationName est nil, le centre de notication envoie lobservateur toutes les notications qui contiennent un objet correspondant anObject. Si anObject est nil, le centre de notication envoie lobservateur toutes les notications nommes notificationName.

Lobservateur nest pas gard par le centre de notication. Vous noterez que la mthode prend en argument un slecteur.
- (void)postNotification:(NSNotification *)notification

Envoie une notication au centre de notication (voir Figure 14.2).

Figure 14.2
Poster une notication. - (void)postNotificationName:(NSString *)aName object:(id)anObject

Cre et poste une notication.


- (void)removeObserver:(id)observer

Retire observer de la liste des observateurs.

Chapitre 14

Notications

213

Poster une notication


Puisque poster une notication constitue ltape la plus simple, nous commencerons par cette opration. Aprs la rception dun message changeBackgroundColor:, lobjet PreferenceController postera une notication contenant la nouvelle couleur. La notication se nommera @"BNRColorChanged", mais nous allons crer une variable globale pour cette constante. Les programmeurs expriments ajoutent un prxe la notication an quelle nentre pas en conit avec dautres notications qui pourraient exister dans lenvironnement de lapplication. Ouvrez PreferenceController.h et ajoutez la dclaration avec les autres constantes de type chane :
extern NSString * const BNRColorChangedNotification;

Dans PreferenceController.m, dnissez la constante :


NSString * const BNRColorChangedNotification = @"BNRColorChanged";

Modiez la mthode changeBackgroundColor: de PreferenceController.m :


- (IBAction)changeBackgroundColor:(id)sender { NSColor *color = [colorWell color]; NSData *colorAsData = [NSKeyedArchiver archivedDataWithRootObject:color]; [[NSUserDefaults standardUserDefaults] setObject:colorAsData forKey:BNRTableBgColorKey]; NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; NSLog(@"Envoi dune notification"); [nc postNotificationName:BNRColorChangedNotification object:self]; }

Inscrire un observateur
Pour inscrire un observateur, nous devons fournir plusieurs lments : lobjet qui constitue lobservateur, les noms des notications qui lintressent et le message envoy lors de larrive dune notication intressante. Nous pouvons galement prciser que seules les notications qui contiennent un certain objet intressent lobservateur. Noubliez pas quil sagit souvent de lobjet qui a post des notications. Par consquent, lorsque nous indiquons que nous sommes intresss par les notications auxquelles est attache une certaine fentre, nous prcisons que seul le redimensionnement de cette fentre prcise nous intresse. Modiez la mthode init de la classe MyDocument :
- (id)init { if (![super init]) return nil;

214

Cocoa

employees = [[NSMutableArray alloc] init]; NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(handleColorChange:) name:BNRColorChangedNotification object:nil]; NSLog(@"Inscrit auprs du centre de notification"); return self; }

Dans la mthode dealloc, retirez linstance de MyDocument du centre de notication :


- (void)dealloc { [self setEmployees:nil]; NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc removeObserver:self]; [super dealloc]; }

Traiter la notication
Lorsque la notication arrive, la mthode handleColorChange: est invoque. Pour le moment, nous consignons simplement cette arrive. Ajoutez la mthode suivante dans le chier MyDocument.m :
- (void)handleColorChange:(NSNotification *)note { NSLog(@"Notification reue: %@", note); }

Compilez et excutez lapplication. Vous devez constater lenvoi et larrive de notications lorsque la couleur est modie dans le panneau Prfrences.

Le dictionnaire userInfo
Si, outre lannonceur, nous souhaitons inclure dautres informations dans la notication, nous devons utiliser le dictionnaire des informations de lutilisateur. Chaque notication possde une variable nomme userInfo laquelle peut tre attach un NSDictionary qui contient les autres informations passer aux observateurs. Dans notre cas, nous voulons ajouter la couleur au dictionnaire userInfo. MyDocument utilisera la couleur lors de larrive de la notication. Dans PreferenceController.m, nous ajoutons un dictionnaire userInfo la notication :
- (IBAction)changeBackgroundColor:(id)sender { NSColor *color = [sender color]; NSData *colorAsData; colorAsData = [NSKeyedArchiver archivedDataWithRootObject:color];

Chapitre 14

Notications

215

[[NSUserDefaults standardUserDefaults] setObject:colorAsData forKey:BNRTableBgColorKey]; NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; NSLog(@"Envoi dune notification"); NSDictionary *d = [NSDictionary dictionaryWithObject:color forKey:@"color"]; [nc postNotificationName:BNRColorChangedNotification object:self userInfo:d]; }

Dans MyDocument.m, nous lisons la couleur partir du dictionnaire userInfo :


- (void)handleColorChange:(NSNotification *)note { NSLog(@"Notification reue: %@", note); NSColor *color = [[note userInfo] objectForKey:@"color"]; [tableView setBackgroundColor:color]; }

Ouvrez plusieurs fentres et modiez la couleur darrire-plan. Vous noterez quelles reoivent toutes la notication et changent immdiatement la couleur.

Pour les plus curieux : dlgus et notications


Lorsquun objet sest dsign comme dlgu dun objet Cocoa standard, il sintresse probablement aux notications mises par cet objet. Par exemple, si nous implmentons un dlgu pour traiter la mthode dlgue windowShouldClose: dune fentre, ce dlgu est certainement intress par les notications NSWindowDidResizeNotification de cette mme fentre. Si un objet Cocoa standard possde un dlgu et poste des notications, il est automatiquement inscrit comme observateur pour les mthodes implmentes par lobjet. Si nous mettons en uvre un tel dlgu, comment connatre le nom de la mthode ? La convention de nommage est simple. Le nom dbute par celui de la notication. Le prxe NS est supprim et la premire lettre est mise en minuscules. Notification est supprim la n. Des deux-points sont ajouts. Par exemple, pour tre averti que la fentre a post une notication NSWindowDidResizeNotification, le dlgu doit implmenter la mthode suivante :
- (void)windowDidResize:(NSNotification *)aNotification

Elle sera invoque automatiquement aprs le redimensionnement de la fentre. Cette mthode est galement mentionne dans la documentation et les chiers den-tte de la classe NSWindow.

216

Cocoa

Exercice
Modiez lapplication pour quelle mette un bip lorsquelle devient active. NSApplication poste une notication NSApplicationDidResignActiveNotification. AppController est un dlgu de NSApplication. NSBeep() produit un bip.

15
Panneaux dalerte
Au sommaire de ce chapitre
Conrmer une suppression

Parfois, nous devons avertir lutilisateur par lintermdiaire dun panneau dalerte. Ces panneaux sont faciles crer. Cocoa est trs orient objet, mais lafchage dun panneau dalerte modal se fait avec la fonction C NSRunAlertPanel(). En voici la dclaration :
int NSRunAlertPanel(NSString *title, NSString *msg, NSString *defaultButton, NSString *alternateButton, NSString *otherButton, ...);

Le code
int choice = NSRunAlertPanel(@"Fido", @"Medor", @"Rex", @"Filou", @"Prince");

afche le panneau dalerte illustr la Figure 15.1.


Figure 15.1
Exemple de panneau dalerte.

Licne du panneau est celle de lapplication responsable. Les deuxime et troisime boutons sont facultatifs. Pour viter lapparition dun bouton, son tiquette doit tre nil.

218

Cocoa

La fonction NSRunAlertPanel() retourne un entier qui indique le bouton sur lequel lutilisateur a cliqu. Il existe des variables globales pour les constantes NSAlertDefaultReturn, NSAlertAlternateReturn et NSAlertOtherReturn.
NSRunAlertPanel() prend un nombre variable darguments. La deuxime chane peut inclure des options de type printf. Les valeurs fournies aprs le paramtre otherButton remplaceront ces options. Ainsi, le code
int choice = NSRunAlertPanel(@"Fido", @"Medor vaut %d", @"Rex", @"Filou", nil, 8);

produit le panneau dalerte illustr la Figure 15.2.


Figure 15.2
Autre exemple de panneau dalerte.

Un panneau dalerte peut tre modal. Autrement dit, les autres fentres de lapplication ne recevront pas les vnements tant que le panneau dalerte naura pas t ferm. Les alertes peuvent galement fonctionner comme des feuilles. Une feuille est une fentre qui souvre devant une autre fentre. Tant que la feuille nest pas ferme, aucun vnement clavier ou souris nest transmis la fentre cache.

Conrmer une suppression


Si lutilisateur clique sur le bouton Supprimer, un panneau dalerte doit apparatre comme une feuille avant que les enregistrements ne soient supprims (voir Figure 15.3).
Figure 15.3
Lapplication termine.

Chapitre 15

Panneaux dalerte

219

Pour obtenir ce comportement, ouvrez MyDocument.nib, slectionnez la vue tableau et afchez linspecteur. Autorisez lutilisateur effectuer plusieurs slections (voir Figure 15.4).

Figure 15.4
Inspecter la vue tableau.

Nous voulons prsent que le bouton Supprimer envoie MyDocument un message qui demandera lutilisateur de conrmer la suppression. Sil valide son choix, MyDocument enverra le message removeEmployee: au contrleur de tableau an quil supprime les objets Person slectionns. Dans Xcode, ouvrez le chier MyDocument.h et ajoutez la mthode dclenche par le bouton Supprimer :
- (IBAction)removeEmployee:(id)sender;

Dans MyDocument.m, implmentez la mthode removeEmployee:, qui afchera le panneau dalerte sous forme dune feuille :
- (IBAction)removeEmployee:(id)sender { NSArray *selectedPeople = [employeeController selectedObjects]; NSAlert *alert = [NSAlert alertWithMessageText:@"Suppression" defaultButton:@"Supprimer" alternateButton:@"Annuler" otherButton:nil informativeTextWithFormat:@"Souhaitez-vous rellement supprimer %d personne(s)?", [selectedPeople count]]; NSLog(@"Affichage de la feuille dalerte"); [alert beginSheetModalForWindow:[tableView window] modalDelegate:self didEndSelector:@selector(alertEnded:code:context:) contextInfo:NULL]; }

220

Cocoa

Cette mthode cre la feuille. Lorsque lutilisateur clique sur un bouton, le message alertEnded:code:context: est envoy lobjet document :
- (void)alertEnded:(NSAlert *)alert code:(int)choice context:(void *)v { NSLog(@"Fin de lalerte"); // Si lutilisateur choisit "Supprimer", demander au contrleur // de tableau de supprimer les personnes. if (choice == NSAlertDefaultReturn) { // Largument de remove: est ignor. // Le contrleur de tableau supprime les objets slectionns. [employeeController remove:nil]; } }

Ouvrez MyDocument.nib. Faites glisser, en maintenant la touche Contrle enfonce, le bouton Supprimer vers licne de Files Owner pour que celui-ci devienne la nouvelle cible. Fixez laction removeEmployee: (voir Figure 15.5).

Figure 15.5
Changer la cible/action du bouton Supprimer.

Compilez et excutez lapplication.

Exercice
Ajoutez la feuille dalerte un autre bouton intitul Garder, mais sans augmentation. Au lieu de supprimer les employs, ce bouton xe simplement les augmentations des employs slectionns zro.

16
Localisation
Au sommaire de ce chapitre
Localiser un chier nib Tables de chanes Pour les plus curieux : ibtool Pour les plus curieux : ordre explicite des options dans les chanes de format

Si lapplication que nous crons est utile, nous voudrons la partager avec le monde entier. Malheureusement, nous ne parlons pas tous la mme langue. Supposons que nous souhaitions proposer notre application RaiseMan des germanophones. Nous dirons alors que nous allons localiser RaiseMan pour les utilisateurs parlant allemand. Si nous crons une application pour le monde entier, nous devons prvoir sa localisation pour, au minimum, les langues suivantes : anglais, franais, espagnol, allemand, hollandais, italien et japonais. Bien videmment, nous ne voulons pas rcrire lintgralit de lapplication pour chaque langue. En ralit, notre objectif est de ne rcrire aucun code Objective-C pour chaque langue. Ainsi, toutes les nations du monde pourront utiliser le mme excutable. Au lieu de crer plusieurs excutables, nous localiserons des ressources et crerons des tables de chanes. Dans le rpertoire du projet, un rpertoire English.lproj contient toutes les ressources pour les anglophones : chiers nib, images et sons. Ce rpertoire est celui cr par dfaut lors dun nouveau projet, ce qui nempche pas dy placer des ressources en franais comme nous lavons fait jusqu prsent. Cependant, il est prfrable et plus lgant de sparer les ressources en fonction des diffrentes langues reconnues. Nous allons donc placer les ressources franaises dans un rpertoire French.lproj. Les chiers nib, les images et les sons de ce rpertoire seront totalement adapts aux francophones. lexcution, lapplication utilisera automatiquement la ressource adapte aux prfrences de langue de lutilisateur.

222

Cocoa

Quels sont les endroits du code de lapplication o nous utilisons la langue ? Par exemple, MyDocument.m contient la ligne suivante
NSAlert *alert = [NSAlert alertWithMessageText:@"Suppression" defaultButton:@"Supprimer" alternateButton:@"Annuler" otherButton:nil informativeTextWithFormat:@"Souhaitez-vous rellement supprimer %d personnes(s)?", [selectedPeople count]];

Pour un public anglophone, la ligne aurait t la suivante :


NSAlert *alert = [NSAlert alertWithMessageText:@"Delete" defaultButton:@"Delete" alternateButton:@"Cancel" otherButton:nil informativeTextWithFormat:@"Do you really want to delete %d people?", [selectedPeople count]];

Pour chaque langue, nous aurons une table de chanes de caractres. Nous demanderons NSBundle de rechercher la chane et il slectionnera automatiquement la version qui correspond aux prfrences de langue de lutilisateur (voir Figure 16.1).
Figure 16.1
Lapplication termine.

Localiser un chier nib


Dans Xcode, slectionnez, sans ouvrir, MyDocument.nib et afchez le panneau des informations. Cliquez sur le bouton Add Localization (voir Figure 16.2). Il vous est demand dindiquer des paramtres rgionaux. Choisissez French.

Chapitre 16

Localisation

223

Figure 16.2
Crer une version franaise de MyDocument.nib.

En utilisant le Finder, nous constatons quune copie de English.lproj/MyDocument.nib a t cre dans French.lproj. Elle contient donc toute linterface dj francise mais dnie dans le rpertoire par dfaut English.lproj. Nous allons reprendre ce dernier pour angliciser lapplication. Dans Xcode, sous le groupe Resources, il existe deux versions de MyDocument.nib : English et French (voir Figure 16.3). Double-cliquez sur la version English pour louvrir dans Interface Builder.
Figure 16.3
Les deux versions de MyDocument.nib.

224

Cocoa

Modiez la fentre pour quelle ressemble celle de la Figure 16.4.


Figure 16.4
Linterface en anglais.

ce stade, nous avons cr une ressource localise. Si nous devons apporter de nombreuses modications au programme, il faudra actualiser les deux chiers nib (la version franaise et la version anglaise). Cest pourquoi il est prfrable dattendre que lapplication soit termine et teste avant de la localiser. Compilez lapplication. Avant de lexcuter, afchez la page International de Prfrences Systme. Choisissez English comme langue favorite. Lancez ensuite votre application. Vous remarquerez que la version anglaise du chier nib est utilise automatiquement. Notez galement que larchitecture de document prend en charge certains lments de la localisation. Par exemple, si vous essayez de fermer un document non enregistr, le message demandant denregistrer ces modications sera en anglais.

Tables de chanes
Pour chaque langue, nous pouvons crer plusieurs tables de chanes. Une table de chanes est un chier qui possde lextension .strings. Par exemple, si nous avons un panneau Rechercher, nous pouvons crer un chier Rechercher.strings pour chaque langue. Ce chier contiendra les phrases utilises par le panneau Rechercher, comme Non trouv. La table de chanes est une simple collection de couples cl-valeur. La cl et la valeur sont des chanes de caractres places entre guillemets, et la paire est termine par un point-virgule :
"Cl1" = "Valeur1"; "Cl2" = "Valeur2";

Chapitre 16

Localisation

225

Pour obtenir la valeur correspondant une certaine cl, nous utilisons NSBundle :
NSBundle *principal = [NSBundle mainBundle]; NSString *uneChaine = [principal localizedStringForKey:@"Cl1" value:@"Valeur1ParDfaut" table:@"Rechercher"];

Ce code recherche la valeur associe "Cl1" dans le chier Rechercher.strings. Si elle nexiste pas dans la langue prfre de lutilisateur, la deuxime langue favorite est examine, et ainsi de suite. Si la cl nexiste dans aucune des langues de lutilisateur, "Valeur1ParDfaut" est retourne. Si le nom de la table nest pas indiqu, Localizable est employ. Les applications les plus simples possdent une seule table de chanes, Localizable.strings, pour chaque langue. Crer des tables de chanes Pour crer un chier Localizable.strings destin aux francophones, choisissez lentre de menu New File dans Xcode. Crez un chier vide et nommez-le Localizable.strings. Enregistrez-le dans le rpertoire French.lproj (voir Figure 16.5).

Figure 16.5
Crer une table de chanes franaise.

Modiez le nouveau chier en y ajoutant le texte suivant :


"DELETE" = "Supprimer"; "SURE_DELETE" = "Souhaitez-vous rellement supprimer %d personne(s)?"; "CANCEL" = "Annuler";

Enregistrez-le, aprs avoir vri quil ne manque aucun point-virgule. Nous allons prsent crer une version anglaise de ce chier. Slectionnez le chier Localizable.strings dans Xcode, afchez le panneau Info et crez la version localise (voir Figure 16.6).

226

Cocoa

Figure 16.6
Crer une table de chanes anglaise.

Modiez le chier de la manire suivante :


"DELETE" = "Delete"; "SURE_DELETE" = "Do you really want to delete %d people?"; "CANCEL" = "Cancel";

Enregistrez le chier. Utiliser la table de chanes Dans une application qui ne contient quune seule table de chanes, voici le code que nous utiliserions :
NSString *deleteString; deleteString = [[NSBundle mainBundle] localizedStringForKey:@"DELETE" value:@"Supprimer" table:nil];

Heureusement, NSBundle.h dnit une macro dans ce but :


#define NSLocalizedString(key, comment) [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]

Chapitre 16

Localisation

227

Le commentaire est totalement ignor par cette macro. En revanche, il est utilis par loutil genstrings, qui parcourt le code la recherche des appels la macro NSLocalizedString et cre un squelette de table de chanes. Cette table de chanes inclut le commentaire. Dans MyDocument.m, recherchez lendroit o le panneau dalerte est cr. Remplacez cette ligne par la suivante :
NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"DELETE", @"Supprimer") defaultButton:NSLocalizedString(@"DELETE", @"Supprimer") alternateButton:NSLocalizedString(@"CANCEL", @"Annuler") otherButton:nil informativeTextWithFormat:NSLocalizedString(@"SURE_DELETE", @"Souhaitez-vous rellement supprimer %d personne(s)?"), [selectedPeople count]];

Compilez lapplication. Choisissez nouveau langlais comme langue favorite dans Prfrences Systme et excutez lapplication. Lorsque vous supprimez une ligne du tableau, vous devez obtenir un panneau dalerte en anglais.

Pour les plus curieux : ibtool


Si nous dveloppons et localisons de nombreuses applications, nous constituerons videmment un ensemble de traductions communes. Il serait trs pratique dinclure automatiquement les chanes traduites dans un chier nib. Il sagit de lun des nombreux usages de ibtool. La commande ibtool, quil faut excuter depuis Terminal, peut recenser les classes et les objets prsents dans un chier nib et afcher les chanes localises prsentes dans un chier plist. Voici comment extraire les chanes localises depuis le chier French.lproj/MyDocument.nib et les placer dans un chier Doc.strings :
> cd RaiseMan/French.lproj > ibtool --generate-stringsfile Doc.strings MyDocument.nib

Le chier Doc.strings rsultant contient plusieurs entres semblables la suivante :


/* Class="NSTableColumn";headerCell.title="Name";ObjectID="100026"; */ "100026.headerCell.title" = "Nom";

Pour crer un dictionnaire espagnol de ce chier nib, nous pouvons modier le chier et traduire les entres en espagnol :
/* Class="NSTableColumn";headerCell.title="Name";ObjectID="100026"; */ "100026.headerCell.title" = "Nombre";

228

Cocoa

Pour remplacer les chanes dun chier nib par leur quivalent espagnol partir de ce dictionnaire, il suft de crer un nouveau chier nib :
> mkdir ../Spanish.lproj > ibtool --strings-file Doc.strings --write ../Spanish.lproj/MyDocument.nib MyDocument.nib

Pour en savoir plus sur ibtool, utilisez la commande Unix man :


> man ibtool

Pour les plus curieux : ordre explicite des options dans les chanes de format
Lorsquun texte est traduit dune langue en une autre, les mots changent, ainsi que leur ordre. Par exemple, dans une langue, les mots pourraient tre agencs de la manire suivante : "Julien veut un scooter". Dans une autre, lordre pourrait tre "Un scooter est demand par Julien". Supposons que nous voulions localiser la chane de format utiliser :
NSString * leFormat = NSLocalizedString(@"VEUT", @"%@ veut un %@"); x = [NSString stringWithFormat:leFormat, @"Julien", @"scooter"];

Dans la premire langue, la ligne suivante fonctionne parfaitement :


"VEUT" = "%@ veut un %@";

Pour la seconde langue, nous devons indiquer explicitement lindice de loption insrer. Pour cela, nous plaons un chiffre et un symbole dollar :
"VEUT" = "Un %2$@ est demand par %1$@".

17
Vues personnalises
Au sommaire de ce chapitre

La hirarchie des vues Dessiner une vue Dessiner avec NSBezierPath NSScrollView Crer des vues par programmation Pour les plus curieux : cellules Pour les plus curieux : isFlipped

Tous les objets visibles dune application sont des fentres ou des vues. Dans ce chapitre, nous crerons une sous-classe de NSView. De temps autre, nous pouvons avoir besoin de crer une vue personnalise pour effectuer des dessins particuliers ou traiter des vnements. Mme si vous navez pas ce type de besoin, vous apprendrez normment sur Cocoa en sachant comment crer une nouvelle classe de vue. Les fentres sont des instances de la classe NSWindow. Chaque fentre possde une collection de vues, chacune responsable dun rectangle de la fentre. La vue est trace lintrieur de ce rectangle et gre les vnements de la souris qui sy produisent. Une vue peut galement traiter les vnements du clavier. Nous avons dj manipul plusieurs sous-classes de NSView : NSButton, NSTextField, NSTableView et NSColorWell sont toutes des vues. Vous remarquerez quune fentre nest pas une sous-classe de NSView.

230

Cocoa

La hirarchie des vues


Les vues sont organises dans une hirarchie (voir Figure 17.1). La fentre dispose dune vue contenu qui occupe intgralement sa zone intrieure. La vue contenu possde gnralement plusieurs vues secondaires, chacune pouvant galement avoir des vues secondaires. Chaque vue connat sa vue suprieure, ses vues infrieures et la fentre dans laquelle elle rside.
Figure 17.1
Hirarchie de vues.

Voici les mthodes correspondantes fournies par NSView :


- (NSView *)superview - (NSArray *)subviews - (NSWindow *)window

Une vue peut possder des vues secondaires, mais ce nest pas le cas de toutes. Les cinq vues suivantes en contiennent gnralement : 1. La vue contenu dune fentre. 2. NSBox. Le contenu dune bote correspond ses vues secondaires. 3. NSScrollView. Une vue qui apparat dans une vue dlement est une vue secondaire dune vue dlement. Les barres de dlement sont galement des vues secondaires de la vue dlement. 4. NSSplitView. Chaque vue dune vue division est une vue secondaire (voir Figure 17.2). 5. NSTabView. Lorsque lutilisateur choisit diffrents onglets, les vues secondaires sont actives et dsactives (voir Figure 17.3).

Chapitre 17

Vues personnalises

231

Figure 17.2
Une vue dlement dans une vue division.

Figure 17.3
Une vue onglet.

Dessiner une vue


Dans cette section, nous allons crer une vue simple qui safche et se dessine ellemme en vert. Elle ressemblera celle illustre la Figure 17.4.
Figure 17.4
Lapplication termine.

Crez un nouveau projet de type Cocoa Application et nommez-le ImageFun.

232

Cocoa

En utilisant lentre de menu File > New File, crez une sous-classe Objective-C NSView subclass nomme StretchView. Crer une instance dune sous-classe de vue Ouvrez MainMenu.nib. Crez une instance de la classe en faisant glisser un emplacement CustomView partir de la bibliothque (sous Views & Cells > Layout View) et en le dposant sur la fentre (voir Figure 17.5).
Figure 17.5
Dposer une vue sur la fentre.

Redimensionnez la vue pour quelle occupe la majeure partie de la fentre. Ouvrez linspecteur Identity et xez la classe de la vue StretchView (voir Figure 17.6).
Figure 17.6
Fixer la classe de la vue StretchView.

Chapitre 17

Vues personnalises

233

Inspecteur Size Lobjet StretchView est une vue secondaire de la vue contenu de la fentre. Ce fait soulve une question intressante : quarrive-t-il la vue lorsque la taille de la vue suprieure change ? Une page du panneau dinformation nous permet de prciser ce comportement. Ouvrez linspecteur Size et appliquez la conguration illustre la Figure 17.7. Dornavant, la taille de la vue intrieure voluera de manire conserver une distance constante entre ses bords et ceux de sa vue suprieure.
Figure 17.7
Redimensionner la vue avec la fentre.

Si nous souhaitons que la vue conserve la mme hauteur, il suft de laisser libre la distance entre le bas de cette vue et celui de la vue suprieure. Nous pouvons galement faire la mme chose pour la distance entre le bord droit de la vue et celui de la fentre. Dans cet exercice, ce nest pas le comportement souhait. Mais, si nous voulions que la vue reste ancre dans le coin suprieur gauche de la fentre, linspecteur serait congur comme le montre la Figure 17.8.
Figure 17.8
Pas cela !

234

Cocoa

La Figure 17.9 prsente le rle des paramtres de linspecteur Size.


Figure 17.9
Signication des lignes rouges dans linspecteur Size.

Enregistrez et fermez le chier nib. drawRect: Lorsque lafchage dune vue a besoin dtre actualis, elle reoit le message drawRect: avec le rectangle qui doit tre dessin ou redessin. La mthode est invoque automatiquement ; nous navons pas lappeler directement. Cependant, si nous savons quune vue doit tre actualise, nous lui envoyons le message setNeedsDisplay: :
[maVue setNeedsDisplay:YES];

Ce message informe maVue quelle est "sale". Aprs le traitement de lvnement, la vue est redessine. Avant dinvoquer drawRect:, le systme verrouille le focus sur la vue. Chaque vue possde son propre contexte graphique, qui inclut le systme de coordonne de la vue, sa couleur actuelle, sa police de caractres actuelle et le rectangle de dcoupe. Lorsque le focus est verrouill sur une vue, son contexte graphique est actif. Lorsquil est dverrouill, le contexte graphique nest plus actif. Les commandes de dessin que nous mettons sont excutes dans le contexte graphique courant.

Chapitre 17

Vues personnalises

235

Nous pouvons utiliser NSBezierPath pour tracer des lignes, des cercles, des courbes et des rectangles. NSImage permet de crer des images composites sur la vue. Dans lexemple suivant, nous remplissons la totalit de la vue avec un rectangle vert. Ouvrez StretchView.m et ajoutez le code suivant dans la mthode drawRect: :
- (void)drawRect:(NSRect)rect { NSRect bounds = [self bounds]; [[NSColor greenColor] set]; [NSBezierPath fillRect:bounds]; }

Comme le montre la Figure 17.10, NSRect est une structure qui contient deux membres : origin, de type NSPoint, et size, de type NSSize.
Figure 17.10
NSRect, NSSize et NSPoint.

NSRect

NS

Si

ze

y: float

origin: NSPoint x: float

NSSize est une structure avec deux membres : width et height (tous deux de type float). NSPoint est une structure avec deux membres : x et y (tous deux de type float).

Pour des raisons de performances, les structures sont parfois utilises la place de classes Objective-C. Voici la liste de toutes les structures Cocoa que vous avez de fortes chances dutiliser : NSSize, NSPoint, NSRect, NSRange, NSDecimal et NSAffineTransformStruct. NSRange sert dnir des intervalles. NSDecimal dcrit des nombres avec

siz e:

height: float

width : float

236

Cocoa

une prcision et une fonction darrondi spciques. NSAffineTransformStruct reprsente des transformations linaires pour les graphiques. La vue connat son emplacement sous forme dun NSRect appel bounds. Dans notre exemple de mthode drawRect:, nous rcuprons le rectangle bounds, xons la couleur courante vert et remplissons lintgralit du rectangle bounds avec la couleur actuelle. Le NSRect pass en argument la vue correspond la rgion qui est "sale" et qui doit donc tre redessine. Si les tracs demandent beaucoup de temps, lactualisation du rectangle sale permet dacclrer considrablement lapplication.
setNeedsDisplay: dclenche lactualisation de la partie visible de la vue. Si nous souhaitons tre plus prcis sur la partie de la vue redessiner, nous utilisons la place setNeedsDisplayInRect: :
NSRect rectSale; rectSale = NSMakeRect(0, 0, 50, 50); [maVue setNeedsDisplayInRect:rectSale];

Compilez et excutez lapplication. Redimensionnez la fentre.

Dessiner avec NSBezierPath


Le trac de lignes, dovales, de courbes ou de polygones se fait avec NSBezierPath. Dans ce chapitre, nous avons dj utilis la mthode de classe fillRect: de NSBezierPath pour colorier la vue. Dans cette section, nous utiliserons NSBezierPath pour tracer des lignes qui relient des points alatoires (voir Figure 17.11).
Figure 17.11
Lapplication termine.

Chapitre 17

Vues personnalises

237

Nous avons tout dabord besoin dune variable dinstance pour contenir linstance de NSBezierPath. Nous allons galement crer une mthode dinstance qui retourne un point alatoire dans la vue. Ouvrez StretchView.h et modiez-le de la manire suivante :
#import <Cocoa/Cocoa.h> @interface StretchView: NSView { NSBezierPath *path; } - (NSPoint)randomPoint; @end

Dans StretchView.m, nous rednissons initWithFrame:. En tant quinitialiseur dsign de NSView, initWithFrame: sera invoque automatiquement lors de la cration dune instance de notre vue. Dans notre version de initWithFrame:, nous allons crer lobjet de chemin et le remplir de lignes connectant des points alatoires. Saisissez le code suivant dans StretchView.m :
#import "StretchView.h" @implementation StretchView - (id)initWithFrame:(NSRect)rect { if (![super initWithFrame:rect]) return nil; // Initialiser le gnrateur de nombres alatoires. srandom(time(NULL)); // Crer un objet de chemin. path = [[NSBezierPath alloc] init]; [path setLineWidth:3.0]; NSPoint p = [self randomPoint]; [path moveToPoint:p]; int i; for (i = 0; i < 15; i++) { p = [self randomPoint]; [path lineToPoint:p]; } [path closePath]; return self; } - (void)dealloc { [path release]; [super dealloc]; } // randomPoint retourne un point alatoire dans la vue. - (NSPoint)randomPoint { NSPoint result; NSRect r = [self bounds];

238

Cocoa

result.x = r.origin.x + random() % (int)r.size.width; result.y = r.origin.y + random() % (int)r.size.height; return result; } - (void)drawRect:(NSRect)rect { NSRect bounds = [self bounds]; // Remplir la vue en vert. [[NSColor greenColor] set]; [NSBezierPath fillRect: bounds]; // Tracer le chemin en blanc. [[NSColor whiteColor] set]; [path stroke]; } @end

Compilez et excutez lapplication. Pas mal, non ? Essayez en remplaant [path stroke] par [path fill].

NSScrollView
Dans le monde de lart, une uvre plus grande cote gnralement plus cher quune uvre plus petite de qualit gale. Notre vue est jolie, mais elle aurait plus de valeur si elle tait plus grande. Comment la rendre plus grande tout en la faisant tenir dans cette petite fentre ? Il suft de la placer dans une vue dlement (voir Figure 17.12).
Figure 17.12
Lapplication termine.

Une vue dlement est constitue de trois parties : la vue document, la vue contenu et les barres de dlement. Dans notre exemple, notre vue deviendra la vue document et sera afche dans la vue contenu, qui est une instance de NSClipView.

Chapitre 17

Vues personnalises

239

Si cette modication semble complique, elle est en fait trs simple. Elle ne demande aucune ligne de code. Ouvrez MainMenu.nib dans Interface Builder. Slectionnez la vue et choisissez Embed Objects in Scroll View dans le menu Layout (voir Figure 17.13).
Figure 17.13
Embarquer StretchView dans une vue dlement.

Lorsque la taille de la fentre change, celle de la vue dlement doit galement suivre, mais pas celle du document. Ouvrez linspecteur Size, slectionnez la vue dlement et congurez linspecteur an quelle change de taille avec la fentre (voir Figure 17.14).
Figure 17.14
Redimensionner la vue dlement avec la fentre.

Notez la largeur et la hauteur de la vue.

240

Cocoa

Pour slectionner la vue document, double-cliquez dans la vue dlement. Lintitul de linspecteur doit devenir Stretch View Size. Redimensionnez la vue pour quelle soit deux fois plus haute et large que la vue dlement. Congurez linspecteur Size pour que cette vue reste ancre dans le coin infrieur gauche de sa vue suprieure et quelle ne change pas de taille (voir Figure 17.15). Compilez et excutez lapplication.
Figure 17.15
Agrandir StrechView, avec une taille ge.

Crer des vues par programmation


En gnral, les vues sont instancies dans Interface Builder. Cependant, une fois ou lautre, nous devrons crer des vues par programmation. Par exemple, supposons que nous ayons un pointeur sur une fentre et souhaitions y ajouter un bouton. Le code suivant cre un bouton et le place dans la vue contenu de la fentre :
NSView *superVue = [window contentView]; NSRect cadre = NSMakeRect(10, 10, 200, 100); NSButton *bouton = [[NSButton alloc] initWithFrame:cadre]; [bouton setTitle:@"Cliquer ici!"]; [superVue addSubview:bouton]; [bouton release];

Pour les plus curieux : cellules


NSControl drive de NSView. Avec son contexte graphique, NSView est un objet relativement grand et coteux instancier. Lorsque la classe NSButton a t cre, la premire application crite par un dveloppeur a t une calculatrice avec 10 lignes et 10 colonnes de boutons. Les performances ntaient pas au rendez-vous, en raison des 100 petites vues. Plus tard, quelquun a eu la brillante ide de dplacer le cerveau du bouton dans un autre objet (non une vue) et de crer une grande vue (nomme NSMatrix) pour

Chapitre 17

Vues personnalises

241

servir de vue aux 100 cerveaux de bouton. La classe contenant le cerveau dun bouton se nomme NSButtonCell (voir Figure 17.16).
Figure 17.16
NSMatrix.

NSButton est nalement devenu une vue qui possdait un NSButtonCell. La cellule de bouton soccupe de tout et NSButton rclame simplement une place dans la fentre (voir Figure 17.17).
Figure 17.17
NSButton et NSButtonCell.

De manire similaire, NSSlider est une vue avec un NSSliderCell, et NSTextField est une vue avec un NSTextFieldCell. En revanche, NSColorWell na pas de cellule. Pour crer une instance de NSMatrix dans Interface Builder, il suft de dposer un contrle possdant une cellule sur la fentre, de choisir Embed Objects In > Matrix et de redimensionner la matrice, en maintenant la touche Option enfonce, jusqu ce quelle contienne le nombre de lignes et de colonnes requises (voir Figure 17.18). Un NSMatrix possde une cible et une action. Une cellule peut galement avoir une cible et une action. Lorsque la cellule est active, sa cible et son action sont utilises. Si la cible et laction de la cellule slectionne ne sont pas xes, celles de la matrice seront employes.

242

Cocoa

Figure 17.18
Une matrice de boutons.

Lorsquon manipule des matrices, il nest pas toujours facile de savoir quelle cellule a t active. Cest pourquoi nous pouvons leur attribuer des balises :
- (IBAction)monAction:(id)sender { id laCellule = [sender selectedCell]; int laBalise = [laCellule tag]; ... }

La balise de la cellule peut tre xe dans Interface Builder. Les cellules sont employes dans plusieurs autres types dobjets. Par exemple, les donnes dun NSTableView sont afches par des cellules.

Pour les plus curieux : isFlipped


PDF et PostScript utilisent tous deux un systme de coordonnes cartsiennes : y augmente vers le haut de la page. Par dfaut, Quartz suit ce modle. Lorigine se trouve normalement dans le coin infrieur gauche de la vue. Pour certains types de tracs, les calculs mathmatiques sont plus simples lorsque lorigine se trouve dans le coin suprieur gauche et que y augmente vers le bas de la page. Dans ce cas, la vue est retourne (ipped). Pour retourner une vue, il suft de rednir isFlipped dans la classe de la vue de manire quelle retourne YES :
- (BOOL)isFlipped { return YES; }

Chapitre 17

Vues personnalises

243

Vous devez savoir que les coordonnes x et y sont mesures en points. En gnral, un point quivaut 1/72 pouces. En ralit, un point est gal un pixel sur lcran. Cependant, il est possible de modier la taille dun point en changeant le systme de coordonnes :
// Doubler la taille de tout le contenu de la vue. NSSize nouvelleEchelle; nouvelleEchelle.width = 2.0; nouvelleEchelle.height = 2.0; [maVue scaleUnitSquareToSize:nouvelleEchelle]; [maVue setNeedsDisplay:YES];

Exercice
NSBezierPath permet galement de tracer des courbes de Bzier. Remplacez les lignes droites par des courbes alatoires. Conseil : consultez la documentation de NSBezierPath.

18
Images et vnements de la souris
Au sommaire de ce chapitre
NSResponder NSEvent Recevoir les vnements de la souris Utiliser NSOpenPanel Intgrer une image dans la vue Systme de coordonnes dune vue Autodlement Pour les plus curieux : NSImage

Dans le chapitre prcdent, nous avons trac des lignes entre des points alatoires. Une application de dessin serait plus intressante, mais il nous faut pour cela grer les vnements de la souris.

NSResponder
NSView drive de NSResponder. Toutes les mthodes de gestion des vnements sont dclares dans NSResponder. Nous aborderons les vnements du clavier au chapitre suivant. Pour le moment, nous nous intressons uniquement aux vnements de la souris. NSResponder dclare les mthodes suivantes : - (void)mouseDown:(NSEvent *)theEvent - (void)rightMouseDown:(NSEvent *)theEvent - (void)otherMouseDown:(NSEvent *)theEvent

246

Cocoa

(void)mouseUp:(NSEvent *)theEvent (void)rightMouseUp:(NSEvent *)theEvent (void)otherMouseUp:(NSEvent *)theEvent (void)mouseDragged:(NSEvent *)theEvent (void)scrollWheel:(NSEvent *)theEvent (void)rightMouseDragged:(NSEvent *)theEvent (void)otherMouseDragged:(NSEvent *)theEvent

Largument est toujours un objet NSEvent.

NSEvent
Un objet dvnement contient toutes les informations concernant laction de lutilisateur qui a dclench lvnement. Pour le traitement des vnements de la souris, les mthodes suivantes sont particulirement utiles :
- (NSPoint)locationInWindow

Retourne lemplacement de lvnement de la souris.


- (unsigned int)modifierFlags

Lentier indique les touches de modication du clavier que lutilisateur a maintenu enfonces au moment du clic. Cela permet au programmeur de diffrencier, par exemple, un Contrle-clic dun Maj+clic. Le code pourrait tre le suivant :
- (void)mouseDown:(NSEvent *)e { unsigned int flags; flags = [e modifierFlags]; if (flags & NSControlKeyMask) { ...grer le Contrle-clic... } if (flags & NSShiftKeyMask) { ...grer le Maj+clic... } }

Voici les constantes habituellement combines par un ET (&) avec lindicateur des touches de modication :
NSShiftKeyMask NSControlKeyMask NSAlternateKeyMask NSCommandKeyMask

Chapitre 18

Images et vnements de la souris

247

- (NSTimeInterval)timestamp

Cette mthode donne lintervalle de temps, en secondes, entre le moment o la machine a dmarr et le moment de lvnement. NSTimeInterval quivaut au type double.
- (NSWindow *)window

Retourne la fentre associe lvnement.


- (int)clickCount

tait-ce un simple, un double ou un triple-clic ?


- (float)pressure

Si lutilisateur se sert dun priphrique dentre qui donne la pression, par exemple une tablette, cette mthode retourne cette information. Il sagit dune valeur entre 0 et 1.
- (float)deltaX - (float)deltaY - (float)deltaZ

Ces mthodes indiquent le changement de la position de la souris ou de la molette de dlement.

Recevoir les vnements de la souris


Pour recevoir les vnements de la souris, nous devons rednir les mthodes de gestion des vnements de la souris dans StretchView.m :
#pragma mark Events - (void)mouseDown:(NSEvent *)event { NSLog(@"mouseDown: %d", [event clickCount]); } - (void)mouseDragged:(NSEvent *)event { NSPoint p = [event locationInWindow]; NSLog(@"mouseDragged:%@", NSStringFromPoint(p)); }

248

Cocoa

- (void)mouseUp:(NSEvent *)event { NSLog(@"mouseUp:"); }

Compilez et excutez lapplication. Essayez un double-clic et vriez le nombre de clics. Notez que le premier clic est afch, puis le second. Le premier clic indique un nombre de clics gal 1, tandis que le second possde un compteur de clics gal 2. Remarquez la directive #pragma mark. Chaque fentre ddition Xcode prsente une liste droulante qui vous permet de sauter nimporte quelle dclaration et dnition dans le chier. #pragma mark place une tiquette dans cette liste droulante. Les programmeurs styls, tels que vous, les utilisent pour grouper leurs mthodes.

Utiliser NSOpenPanel
Il serait amusant de composer une image sur la vue, mais nous devons dabord crer un objet contrleur qui lira les donnes de limage partir dun chier. Cest une bonne occasion dapprendre utiliser NSOpenPanel. Lapplication RaiseMan employait NSOpenPanel, mais de manire automatique par la classe NSDocument. Dans cette section, nous allons lutiliser explicitement. La Figure 18.1 prsente notre application aprs que lutilisateur a choisi une image.
Figure 18.1
Lapplication termine.

Le curseur plac en bas de la fentre permettra de contrler la transparence de limage. La Figure 18.2 illustre le graphe dobjets.

Chapitre 18

Images et vnements de la souris

249

NSMenuItem action = showOpenPanel: target

NSSlider

NSBezierPath

NSImage

value = opacity AppController - showOpenPanel: - openPanelDidEnd: returnCode: contextInfo:

path

image

StretchView stretchView opacity - setImage: - randomPoint

Figure 18.2
Graphe dobjets.

Modier le chier nib Dans Xcode, crez une nouvelle classe Objective-C nomme AppController. Dans AppController.h, ajoutez une outlet pour StretchView et une action qui afchera le panneau douverture de chier :
#import <Cocoa/Cocoa.h> @class StretchView; @interface AppController: NSObject { IBOutlet StretchView *stretchView; } - (IBAction)showOpenPanel:(id)sender;

Ouvrez MainMenu.nib. Faites glisser un objet depuis la bibliothque vers la fentre du chier nib. Dans linspecteur Identity, xez sa classe AppController (voir Figure 18.3).

Figure 18.3
Crer un AppController.

250

Cocoa

Dposez un curseur sur la fentre. Dans linspecteur, attribuez-lui un intervalle de 0 1. Cochez galement la case intitule Continuous. Ce curseur contrlera la transparence de limage (voir Figure 18.4).
Figure 18.4
Inspecter le curseur.

Liez la valeur du curseur au chemin de cl stretchView.opacity dAppController (voir Figure 18.5).


Figure 18.5
Lier la valeur du curseur.

Faites un Contrle-clic sur AppController et connectez loutlet stretchView au StretchView de la fentre (voir Figure 18.6).

Chapitre 18

Images et vnements de la souris

251

Figure 18.6
Connecter loutlet stretchView.

Examinez le menu principal du chier nib. Ouvrez le menu File et supprimez toutes les entres lexception dOpen. Connectez cette entre de menu laction showOpen-Panel: dAppController (voir Figure 18.7). Enregistrez le chier.

Figure 18.7
Connecter lentre de menu.

252

Cocoa

Modier le code Modiez AppController.m :


#import "AppController.h" #import "StretchView.h" @implementation AppController - (void)openPanelDidEnd:(NSOpenPanel *)openPanel returnCode:(int)returnCode contextInfo:(void *)x { // Lentre "Open" a-t-elle t choisie? if (returnCode == NSOKButton) { NSString *path = [openPanel filename]; NSImage *image = [[NSImage alloc] initWithContentsOfFile:path]; [stretchView setImage:image]; [image release]; } } - (IBAction)showOpenPanel:(id)sender { NSOpenPanel *panel = [NSOpenPanel openPanel]; // Afficher le panneau Open. [panel beginSheetForDirectory:nil file:nil types:[NSImage imageFileTypes] modalForWindow:[stretchView window] modalDelegate:self didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; } @end

Examinons la ligne dafchage de la feuille. Cette mthode est trs pratique :


- (void)beginSheetForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)types modalForWindow:(NSWindow *)docWindow modalDelegate:(id)delegate didEndSelector:(SEL)didEndSelector contextInfo:(void *)contextInfo

Elle afche un panneau Ouvrir sous forme dune feuille attache docWindow. Le paramtre didEndSelector doit possder la signature suivante :
- (void)openPanelDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;

Chapitre 18

Images et vnements de la souris

253

Elle doit tre implmente dans le dlgu modal. path correspond au chemin sur lequel le navigateur de chier doit souvrir. name prcise le nom du chier choisi initialement. Ces deux paramtres peuvent tre nil.

Intgrer une image dans la vue


Nous devons galement modier la vue StretchView an quelle utilise opacity et image. Tout dabord, dclarons des variables et des mthodes dans le chier StretchView.h :
#import <Cocoa/Cocoa.h> @interface StretchView: NSView { NSBezierPath *path; NSImage *image; float opacity; } @property (readwrite) float opacity; - (void)setImage:(NSImage *)newImage; - (NSPoint)randomPoint; @end

Implmentons-les dans le chier StretchView.m :


#pragma mark Accessors - (void)setImage:(NSImage *)newImage { [newImage retain]; [image release]; image = newImage; [self setNeedsDisplay:YES]; } - (float)opacity { return opacity; } - (void)setOpacity:(float)x { opacity = x; [self setNeedsDisplay:YES]; }

la n de chaque mthode, nous informons la vue quelle doit se redessiner. Vers la n dinitWithFrame, nous xons lopacit 1.0 :
[path closePath]; opacity = 1.0; return self; }

254

Cocoa

Dans StretchView.m, nous devons ajouter la composition de limage dans la mthode drawRect: :
- (void)drawRect:(NSRect)rect { NSRect bounds = [self bounds]; [[NSColor greenColor] set]; [NSBezierPath fillRect:bounds]; [[NSColor whiteColor] set]; [path fill]; if (image) { NSRect imageRect; imageRect.origin = NSZeroPoint; imageRect.size = [image size]; NSRect drawingRect = imageRect; [image drawInRect:drawingRect fromRect:imageRect operation:NSCompositeSourceOver fraction:opacity]; } }

Notez que la mthode drawInRect:fromRect:operation:fraction: compose limage sur la vue. Largument fraction dtermine son opacit. Nous devons librer limage dans la mthode dealloc :
- (void)dealloc { [path release]; [image release]; [super dealloc]; }

Compilez et excutez lapplication. Vous trouverez quelques images dans /Developer/ Examples/AppKit/Sketch. Lorsque vous ouvrez une image, elle safche dans le coin infrieur gauche de lobjet StretchView.

Systme de coordonnes dune vue


La dernire petite touche consiste choisir lemplacement et les dimensions de limage laide de la souris. mouseDown positionnera un angle du rectangle dans lequel limage apparatra, tandis que mouseUp indiquera langle oppos. Lapplication nale ressemblera celle illustre la Figure 18.8. Chaque vue possde son propre systme de coordonnes. Par dfaut, le point (0, 0) correspond au coin infrieur gauche. Cela correspond au systme employ par les formats PDF et PostScript. Nous pouvons modier le systme de coordonnes de la vue. Il est possible de dplacer lorigine, de changer lchelle ou de tourner les coordonnes. La fentre dispose galement de son systme de coordonnes.

Chapitre 18

Images et vnements de la souris

255

Figure 18.8
Lapplication termine.

Si nous disposons de deux vues, a et b, voici comment convertir un NSPoint p du systme de coordonnes de b dans celui de a :
NSPoint q = [a convertPoint:p fromView:b];

Si b vaut nil, le point est converti dans le systme de coordonnes de la fentre. Les vnements de la souris sont donns dans le systme de coordonnes de la fentre. Nous devons donc pratiquement toujours convertir le point dans le systme de coordonnes local. Nous dnissons des variables pour stocker les angles du rectangle dans lequel sera dessine limage. Ajoutez ces variables dinstance dans StretchView.h :
NSPoint downPoint; NSPoint currentPoint;

Lemplacement de lvnement mouseDown: sera indiqu dans downPoint, et currentPoint sera actualis par mouseDragged: et mouseUp:. Modiez les mthodes de traitement des vnements de la souris an de mettre jour les variables downPoint et currentPoint :
- (void)mouseDown:(NSEvent *)event { NSPoint p = [event locationInWindow]; downPoint = [self convertPoint:p fromView:nil]; currentPoint = downPoint; [self setNeedsDisplay:YES]; }

256

Cocoa

- (void)mouseDragged:(NSEvent *)event { NSPoint p = [event locationInWindow]; currentPoint = [self convertPoint:p fromView:nil]; [self setNeedsDisplay:YES]; } - (void)mouseUp:(NSEvent *)event { NSPoint p = [event locationInWindow]; currentPoint = [self convertPoint:p fromView:nil]; [self setNeedsDisplay:YES]; }

Ajoutez une mthode pour calculer le rectangle partir de deux points :


- (NSRect)currentRect { float minX = MIN(downPoint.x, float maxX = MAX(downPoint.x, float minY = MIN(downPoint.y, float maxY = MAX(downPoint.y,

currentPoint.x); currentPoint.x); currentPoint.y); currentPoint.y);

return NSMakeRect(minX, minY, maxX-minX, maxY-minY); }

Il est tonnant de constater que nombreux sont ceux faire des erreurs dans cette dernire mthode. Examinez votre code plusieurs fois avant de poursuivre. Sil nest pas correct, les rsultats seront assez dcevants. La mthode currentRect est dclare dans StretchView.h. Pour que lutilisateur voie quelque chose mme sil na pas dni le rectangle, nous initialisons downPoint et currentPoint dans la mthode setImage: :
- (void)setImage:(NSImage *)newImage { [newImage retain]; [image release]; image = newImage; NSSize imageSize = [newImage size]; downPoint = NSZeroPoint; currentPoint.x = downPoint.x + imageSize.width; currentPoint.y = downPoint.y + imageSize.height; [self setNeedsDisplay:YES]; }

Dans la mthode drawRect:, limage est construite dans le rectangle :


- (void)drawRect:(NSRect)rect { NSRect bounds = [self bounds]; [[NSColor greenColor] set]; [NSBezierPath fillRect:bounds]; [[NSColor whiteColor] set]; [path stroke];

Chapitre 18

Images et vnements de la souris

257

if (image) { NSRect imageRect; imageRect.origin = NSZeroPoint; imageRect.size = [image size]; NSRect drawingRect = [self currentRect]; [image drawInRect:drawingRect fromRect:imageRect operation:NSCompositeSourceOver fraction:opacity]; } }

Compilez et excutez lapplication. Vous remarquerez que la vue ne dle pas lorsque le rectangle en dborde. Il serait intressant de faire dler la vue an que lutilisateur puisse voir le rsultat de son opration. Cette technique se nomme autodlement.

Autodlement
Pour ajouter le dlement automatique lapplication, nous devons envoyer le message autoscroll: la vue division lorsque lutilisateur dnit le rectangle. Nous inclurons lvnement en argument. Ouvrez StretchView.m et ajoutez la ligne suivante la mthode mouseDragged: :
- (void)mouseDragged:(NSEvent *)event { NSPoint p = [event locationInWindow]; currentPoint = [self convertPoint:p fromView:nil]; [self autoscroll:event]; [self setNeedsDisplay:YES]; }

Compilez et excutez lapplication. Notez que le dlement automatique se produit uniquement pendant le dplacement de la souris. Pour que lautodlement soit plus harmonieux, les programmeurs crent gnralement une minuterie qui envoie priodiquement la mthode autoscroll: la vue pendant que lutilisateur dnit le rectangle. Les minuteries seront prsentes au Chapitre 24.

Pour les plus curieux : NSImage


Dans la plupart des cas, lopration se limite lire une image, la redimensionner et lintgrer dans une vue, comme nous lavons fait dans ce chapitre. Un objet NSImage possde un tableau de reprsentations. Par exemple, notre image pourrait reprsenter une vache. Ce dessin pourrait tre au format PDF, une bitmap en couleurs et une bitmap en noir et blanc. Chacune de ces variantes est une instance dune sous-classe de NSImageRep. Nous pouvons ajouter et retirer des reprsentations de notre image. Lorsque nous voudrons rcrire Adobe Photoshop, nous manipulerons les reprsentations de limage.

258

Cocoa

Voici la liste des sous-classes de NSImageRep :


m m m m m m

NSBitmapImageRep ; NSEPSImageRep ; NSPICTImageRep ; NSCachedImageRep ; NSCustomImageRep ; NSPDFImageRep.

Mme sil nexiste que cinq sous-classes de NSImageRep, il est important de noter que NSImage sait lire environ une vingtaine de types de chiers dimage, y compris les formats classiques PICT, GIF, JPG, PNG, PDF, BMP, TIFF, etc.

Exercice
Crez une nouvelle application fonde sur des documents qui permettent lutilisateur de dessiner des ovales de nimporte quelle taille des emplacements quelconques. NSBezierPath fournit la mthode suivante :
+ (NSBezierPath *)bezierPathWithOvalInRect:(NSRect)rect

Si vous tes ambitieux, ajoutez une fonction denregistrement et de lecture de chiers. Si vous tes vraiment ambitieux, ajoutez une fonction dannulation.

19
vnements du clavier
Au sommaire de ce chapitre
NSResponder NSEvent Nouveau projet avec une vue personnalise Pour les plus curieux : effets de survol La bote bleue oue

Lorsque lutilisateur tape sur son clavier, o sont donc envoys les vnements associs ? Tout dabord, le gestionnaire de fentres reoit lvnement et le redirige vers lapplication active. Celle-ci retransmet les vnements du clavier la fentre active. Celle-ci dirige lvnement vers la vue "active". Mais quelle est la vue active ? Chaque fentre possde une outlet appele firstResponder qui pointe sur une vue de cette fentre. Il sagit de la vue "active" pour cette fentre. Par exemple, lorsquon clique sur un champ de texte, il devient le firstResponder de la fentre (voir Figure 19.1). Lorsque lutilisateur change de firstResponder en appuyant sur la touche Tab ou en cliquant dans une autre vue, les vues procdent un certain rituel avant que loutlet firstResponder soit modie. Tout dabord, la vue qui va devenir le firstResponder est consulte pour savoir si elle accepte ce rle. Si elle retourne NO, cela signie quelle nest pas intresse par les vnements du clavier. Par exemple, la saisie ntant pas gre par un curseur, il refuse daccepter la fonction de premier rpondeur. Si la vue accepte, la vue qui est actuellement le premier rpondeur est consulte pour savoir si elle accepte de dmissionner de ce poste. Si ldition nest pas termine, la vue peut refuser. Par exemple, si lutilisateur na pas ni de saisir son numro de tlphone, le champ de texte peut refuser. Enn, la vue reoit le rle de premier rpondeur. Trs souvent, cela dclenche un changement dapparence (voir Figure 19.2).

260

Cocoa

Figure 19.1
Le premier rpondeur de la fentre active est"actif".

Figure 19.2
Devenir le premier rpondeur.

Chapitre 19

vnements du clavier

261

Chaque fentre possde son propre premier rpondeur. Plusieurs fentres peuvent tre ouvertes, mais seul le premier rpondeur de la fentre active reoit les vnements du clavier.

NSResponder
Les mthodes suivantes, hrites de NSResponder, nous intressent particulirement :
- (BOOL)acceptsFirstResponder

Une sous-classe rednit cette mthode an de retourner YES si elle gre les vnements du clavier.
- (BOOL)resignFirstResponder

Demande au destinataire sil est prt quitter son poste de premier rpondeur.
- (BOOL)becomeFirstResponder

Indique au destinataire quil est devenu le premier rpondeur dans son NSWindow.
- (void)keyDown:(NSEvent *)theEvent

Informe le destinataire que lutilisateur a appuy sur une touche.


- (void)keyUp:(NSEvent *)theEvent

Informe le destinataire que lutilisateur a relch une touche.


- (void)flagsChanged:(NSEvent *)theEvent

Informe le destinataire que lutilisateur a appuy sur une touche de modication (par exemple Maj ou Contrle) ou la relche.

NSEvent
Au chapitre prcdent, nous avons abord NSEvent du point de vue des vnements de la souris. Voici les mthodes les plus utilises pour obtenir des informations concernant un vnement du clavier :
- (NSString *)characters

Retourne les caractres crs par lvnement.


- (BOOL)isARepeat

Retourne YES si la touche est une rptition (lutilisateur a maintenu la touche enfonce). Retourne NO si lvnement concerne une nouvelle touche.

262

Cocoa

- (unsigned short)keyCode

Retourne le code de la touche du clavier qui se trouve lorigine de lvnement.


- (unsigned int)modifierFlags

Retourne un champ de bits entier qui indique au destinataire les touches de modication en vigueur. Pour plus dinformations sur la signication des bits de lentier, consultez le Chapitre 18.

Nouveau projet avec une vue personnalise


Crez un nouveau projet de type Cocoa Application ayant pour nom TypingTutor. Dans Xcode, crez une classe Objective-C NSView subclass nomme BigLetterView. Agencer linterface Ouvrez MainMenu.nib. Crez une instance de la classe en faisant glisser un emplacement CustomView (sous Views & Cells > Layout Views) sur la fentre (voir Figure 19.3).
Figure 19.3
Dposer une vue sur la fentre.

Dans linspecteur Identity, xez la classe de la vue BigLetterView (voir Figure 19.4). Dposez deux champs de texte (sous Views & Cells > Input & Values) sur la fentre (voir Figure 19.5).

Chapitre 19

vnements du clavier

263

Figure 19.4
Fixer la classe de la vue BigLetterView.

Figure 19.5
Linterface termine.

tablir les connexions Nous devons prsent crer la boucle dactivation des vues dans notre fentre. Autrement dit, nous dterminons lordre dans lequel les vues seront slectionnes lorsque lutilisateur appuiera sur la touche Tab pour passer dun contrle au suivant. Nous choisissons le champ de texte gauche, puis le champ de texte droite, puis la vue BigLetterView, pour revenir ensuite au champ de texte gauche. Affectez le champ de texte de droite la variable nextKeyView du champ de texte de gauche (voir Figure 19.6). Affectez la vue BigLetterView la variable nextKeyView du champ de texte de droite (voir Figure 19.7).

264

Cocoa

Figure 19.6
Affecter la variable nextKeyView du champ de texte de gauche.

Figure 19.7
Affecter la variable nextKeyView du champ de texte de droite.

Enn, affectez le champ de texte de gauche la variable nextKeyView de la vue BigLetterView (voir Figure 19.8). Toute cette procdure permet lutilisateur de

Chapitre 19

vnements du clavier

265

passer dune vue lautre. En appuyant simultanment sur les touches Maj et Tab, la slection se fait dans lordre inverse.
Figure 19.8
Affecter la variable nextKeyView de la vue BigLetterView.

Quelle vue sera le premier rpondeur au moment de lafchage de la fentre ? Pour que ce soit BigLetterView, cliquez du bouton droit sur licne de la fentre dans la fentre du chier nib et xez loutlet initialFirstResponder BigLetterView (voir Figure 19.9).
Figure 19.9
Fixer le premier rpondeur initial de la fentre.

Enregistrez et fermez le chier nib.

266

Cocoa

crire le code Dans cette section, nous allons crire le code de rponse de BigLetterView aux vnements du clavier. Cette vue va galement accepter le rle de premier rpondeur. Les caractres saisis par lutilisateur apparatront sur la console. Lapplication termine ressemblera celle illustre la Figure 19.10.
Figure 19.10
Lapplication termine.

Dans BigLetterView.h Notre BigLetterView dnit deux variables dinstance, avec les mthodes accesseurs correspondantes. La variable bgColor identie la couleur darrire-plan de la vue ; il sagit dun objet NSColor. La variable string contient la dernire lettre saisie par lutilisateur ; il sagit dun objet NSString :
#import <Cocoa/Cocoa.h> @interface BigLetterView: NSView { NSColor *bgColor; NSString *string; } @property (retain, readwrite) NSColor *bgColor; @property (copy, readwrite) NSString *string; @end

Dans BigLetterView.m Linitialiseur dsign dune vue est initWithFrame:. Dans cette mthode, nous appelons la mthode initWithFrame: de la classe mre et attribuons bgColor et string leur valeur par dfaut. Ajoutez les mthodes suivantes dans BigLetterView.m :
- (id)initWithFrame:(NSRect)rect { if (![super initWithFrame:rect]) return nil;

Chapitre 19

vnements du clavier

267

NSLog(@"Initialisation de la vue."); bgColor = [[NSColor yellowColor] retain]; string = @" "; return self; } - (void)dealloc { [bgColor release]; [string release]; [super dealloc]; }

Crez les mthodes accesseurs pour bgColor et string :


#pragma mark Accessors - (void)setBgColor:(NSColor *)c { [c retain]; [bgColor release]; bgColor = c; [self setNeedsDisplay:YES]; } - (NSColor *)bgColor { return bgColor; } - (void)setString:(NSString *)c { c = [c copy]; [string release]; string = c; NSLog(@"La chane est prsent %@", string); } - (NSString *)string { return string; }

Ajoutez le code suivant la mthode drawRect:. Il remplit la vue en utilisant la couleur dnie par bgColor. Si elle tient le rle de premier rpondeur de la fentre, la vue dessine un rectangle bleu autour de son cadre an dindiquer lutilisateur quelle reoit les vnements du clavier :
- (void)drawRect:(NSRect)rect { NSRect bounds = [self bounds]; [bgColor set]; [NSBezierPath fillRect:bounds]; // Suis-je le premier rpondeur de la fentre? if ([[self window] firstResponder] == self) {

268

Cocoa

[[NSColor keyboardFocusIndicatorColor] set]; [NSBezierPath setDefaultLineWidth:4.0]; [NSBezierPath strokeRect:bounds]; } }

Le systme peut optimiser le trac sil sait que la vue est totalement opaque. Nous rednissons la mthode isOpaque de NSView :
- (BOOL)isOpaque { return YES; }

Voici les mthodes pour devenir le premier rpondeur :


- (BOOL)acceptsFirstResponder { NSLog(@"Accepter"); return YES; } - (BOOL)resignFirstResponder { NSLog(@"Dmissionner"); [self setNeedsDisplay:YES]; return YES; } - (BOOL)becomeFirstResponder { NSLog(@"Devenir"); [self setNeedsDisplay:YES]; return YES; }

Aprs tre devenue le premier rpondeur, la vue traite les vnements du clavier. Pour la plupart des keyDown, la vue enregistre simplement la saisie de lutilisateur dans string. En revanche, si lutilisateur appuie sur Tab ou Maj+Tab, la vue demande la fentre de changer son premier rpondeur.
NSResponder (dont drive NSView) offre une mthode nomme interpretKey-Events:. Pour la majorit des vnements du clavier, elle demande simplement la vue dinsrer le texte correspondant. Pour les vnements qui peuvent dclencher une action, comme Tab ou Maj+Tab, elle appelle certaines de ses mthodes.

Dans keyDown:, nous invoquons simplement interpretKeyEvents: :


- (void)keyDown:(NSEvent *)event { [self interpretKeyEvents:[NSArray arrayWithObject:event]]; }

Chapitre 19

vnements du clavier

269

Nous devons ensuite rednir les mthodes appeles par interpretKeyEvents: :


- (void)insertText:(NSString *)input { // Placer le texte saisi par lutilisateur dans string. [self setString:input]; } - (void)insertTab:(id)sender { [[self window] selectKeyViewFollowingView:self]; } // Attention lcriture de "backtab", qui est considr // comme un seul mot. - (void)insertBacktab:(id)sender { [[self window] selectKeyViewPrecedingView:self]; } - (void)deleteBackward:(id)sender { [self setString:@" "]; } @end

Compilez et excutez lapplication. Vous devez constater que la vue devient le premier rpondeur. Pendant quelle tient ce rle, elle doit recevoir les vnements du clavier et les consigner sur le terminal. Notez galement que Tab et Maj+Tab bascule entre les vues (voir Figure 19.11).
Figure 19.11
Lapplication termine.

Vous constaterez quacceptsFirstResponder est invoque bien plus souvent que vous auriez pu le penser lorsque la vue est slectionne.

270

Cocoa

Pour les plus curieux : effets de survol


Au Chapitre 18, nous navons pas examin trois vnements de la souris : mouseMoved:, mouseEntered: et mouseExited:.
- (void)mouseMoved:(NSEvent *)event

Pour recevoir mouseMoved:, la fentre doit accepter les vnements de dplacement de la souris. Lorsque cest le cas, le message mouseMoved: est envoy au premier rpondeur de la fentre. Pour quelle accepte ces vnements, nous devons lui envoyer le message setAcceptsMouseMovedEvents: :
[[self window] setAcceptsMouseMovedEvents:YES];

Par la suite, la vue recevra le message chaque fois que la souris sera dplace. Cela reprsente un grand nombre dvnements. Lorsquune personne minterroge sur la gestion des vnements de dplacement de la souris, je lui demande toujours ce quelle veut en faire. En gnral, elle rpond "des effets de survol". Les effets de survol sont trs rpandus dans les navigateurs web. Lorsque le pointeur de la souris passe au-dessus dune rgion, lapparence de celle-ci change an dindiquer lutilisateur quun clic ce moment-l sera trait par cette rgion. Par exemple, les signets de Safari sont surligns lorsque le pointeur les survole. En gnral, la mise en uvre des effets de survol ne repose pas sur mouseMoved:. la place, nous crons une zone de suivi et rednissons mouseEntered: et mouseExited:. Lorsquune vue est place sur une fentre, viewDidMoveToWindow est invoque. Cette mthode reprsente le bon endroit pour crer des zones de suivi. En passant en paramtre le NSTrackingInVisibleRect, la zone de suivi correspondra automatiquement au rectangle visible du propritaire :
- (void)viewDidMoveToWindow { int options = NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect; NSTrackingArea *ta; ta = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:options owner:self userInfo:nil]; [self addTrackingArea:ta]; [ta release]; }

Chapitre 19

vnements du clavier

271

Nous modions ensuite lapparence lors de linvocation de mouseEntered: et de mouseExited:. En supposant quil existe une variable isHighlighted de type BOOL, voici le code correspondant :
- (void)mouseEntered:(NSEvent *)theEvent { isHighlighted = YES; [self setNeedsDisplay:YES]; } - (void)mouseExited:(NSEvent *)theEvent { isHighlighted = NO; [self setNeedsDisplay:YES]; }

Il suft ensuite dexaminer isHighlighted dans la mthode drawRect: et de dessiner la vue en consquence.

La bote bleue oue


La vue BigLetterView est entoure dune bote bleue lorsquelle devient le premier rpondeur. Cependant, cet entourage nest pas aussi joli que celui des champs de texte. Obtenir la bote bleue oue demande un peu de travail. La bote bleue est dessine par la mthode drawRect: dans BigLetterView.m. Modiez le code de la manire suivante :
if (([[self window] firstResponder] == self) && [NSGraphicsContext currentContextDrawingToScreen]) { [NSGraphicsContext saveGraphicsState]; NSSetFocusRingStyle(NSFocusRingOnly); [NSBezierPath fillRect:bounds]; [NSGraphicsContext restoreGraphicsState]; }

Lorsque la vue quitte son poste de premier rpondeur, elle doit tre redessine, ainsi que la zone occupe par le dgrad de bleu :
- (BOOL)resignFirstResponder { NSLog(@"Dmissionner"); [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]]; return YES; }

Compilez et excutez lapplication.

20
Attributs de texte
Au sommaire de ce chapitre
NSFont NSAttributedString Afcher des chanes et des chanes avec attributs Afcher les lettres Gnrer des donnes PDF partir de la vue Pour les plus curieux : NSFontManager

Ltape suivante consiste afcher la chane dans la vue. la n de ce chapitre, lapplication ressemblera celle illustre la Figure 20.1. Le caractre afch changera en fonction de la saisie.
Figure 20.1
Lapplication termine.

274

Cocoa

NSFont
La classe NSFont noffre, globalement, que deux types de mthodes : 1. Des mthodes de classes pour obtenir la police de caractres souhaite. 2. Des mthodes pour obtenir des informations de mtrique sur la police, comme la hauteur dune lettre. Voici les mthodes de NSFont les plus utilises :
+ (NSFont *)fontWithName:(NSString *)fontName size:(float)fontSize

Cette mthode retourne un objet de police de caractres. fontName indique le nom de la police, comme HelveticaBoldOblique ou Times-Roman. Si fontSize vaut 0.0, cette mthode utilise la taille de police par dfaut dnie par lutilisateur.
+ (NSFont *)userFixedPitchFontOfSize:(float)fontSize + (NSFont *)userFontOfSize:(float)fontSize + (NSFont *)messageFontOfSize:(float)fontSize + (NSFont *)toolTipsFontOfSize:(float)fontSize + (NSFont *)titleBarFontOfSize:(float)fontSize

Ces mthodes retournent la police par dfaut de lutilisateur pour les types de chanes correspondants. nouveau, la taille 0.0 permet dobtenir une police de la taille par dfaut.

NSAttributedString
Parfois, nous voudrons afcher une chane en appliquant certains attributs certains caractres. Par exemple, supposons que nous voulions afcher la chane "Big Nerd Ranch" en soulignant les lettres 0 2, en mettant les lettres 0 7 en vert et en plaant les lettres 9 13 en exposant. Pour manipuler une plage de nombres, Cocoa utilise la structure NSRange, dont les deux membres, location et length, sont des entiers. location correspond lindice du premier lment, length est le nombre dlments dans la plage. La fonction NSMakeRange() cre un NSRange.

Chapitre 20

Attributs de texte

275

Pour crer des chanes de caractres avec des attributs appliqus une plage de caractres, Cocoa dispose des classes NSAttributedString et NSMutableAttributedString. Voici comment crer le NSAttributedString correspondant notre description prcdente :
NSMutableAttributedString *s; s = [[NSMutableAttributedString alloc] initWithString:@"Big Nerd Ranch"]; [s addAttribute:NSFontAttributeName value:[NSFont userFontOfSize:22] range:NSMakeRange(0, 14)]; [s addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:1] range:NSMakeRange(0,3)]; [s addAttribute:NSForegroundColorAttributeName value:[NSColor greenColor] range:NSMakeRange(0, 8)]; [s addAttribute:NSSuperscriptAttributeName value:[NSNumber numberWithInt:-1] range:NSMakeRange(9,5)];

Nous pouvons faire plusieurs choses avec une chane possdant des attributs :
[s drawInRect:[self bounds]]; // Linsrer dans un champ de texte. [textField setAttributedStringValue:s]; // La placer sur un bouton. [button setAttributedTitle:s];

La Figure 20.2 montre linsertion de la chane dans un bouton.


Figure 20.2
Utiliser la chane avec attributs.

276

Cocoa

Voici les variables globales qui correspondent aux attributs les plus utiliss, avec leur type et leur valeur par dfaut :
NSFontAttributeName NSForegroundColorAttributeName NSParagraphStyleAttributeName NSUnderlineColorAttributeName NSUnderlineStyleAttributeName NSSuperscriptAttributeName NSShadowAttributeName

Police de caractres, Helvetica 12 points Couleur, noir


NSParagraphStyle, style de paragraphe standard

Couleur, celle du premier plan Nombre, 0 ou pas de soulignement Nombre, 0 ou pas dexposant ni dindice
NSShadow, nil (pas dombre)

Le chier <AppKit/NSAttributedString.h> contient une liste de tous les attributs. Pour crer des chanes avec attributs, le plus simple consiste utiliser un chier. NSAttributedString accepte de lire et dcrire les formats de chiers suivants :
m m

Une chane. Lecture dun chier texte. RTF. Le format Rich Text Format est un standard pour lcriture dun texte avec plusieurs polices et couleurs. Dans notre cas, nous lirons et xerons le contenu de la chane avec attributs laide dune instance de NSData. RTFD. Il sagit de RTF avec des pices jointes. Outre les multiples polices et couleurs de RTF, il est possible dajouter des images. HTML. La chane avec attributs peut procder une mise en page HTML basique, mais il est prfrable dutiliser WebView pour une meilleure qualit. Word. La chane avec attributs peut lire et crire des documents .doc simples. OpenOfce.

m m

Lors de la lecture dun document, nous voulons reconnatre certaines caractristiques, comme la taille de la page. Si nous fournissons un endroit o la mthode peut placer un pointeur sur un dictionnaire, celui-ci contiendra toutes les informations quil peut obtenir partir des donnes. Par exemple :
NSDictionary *monDict; NSData *donnees = [NSData dataWithContentsOfFile:@"monfichier.rtf"]; NSAttributedString *uneChaine; aString = [[NSAttributedString alloc] initWithRTF:donnees documentAttributes:&monDict];

Si les attributs du document ne nous intressent pas, nous passons simplement NULL.

Chapitre 20

Attributs de texte

277

Afcher des chanes et des chanes avec attributs


NSString et NSAttributedString offrent toutes deux des mthodes qui permettent de les afcher dans une vue. Voici celles de NSAttributedString :
- (void)drawAtPoint:(NSPoint)aPoint

Dessine le destinataire, en utilisant aPoint comme angle infrieur gauche de la chane.


- (void)drawInRect:(NSRect)rect

Dessine le destinataire. Le trac se fait lintrieur de rect. Si ce rectangle est trop petit pour contenir lintgralit de la chane, lafchage est rogn aux limites de rect.
- (NSSize)size

Retourne la taille que le destinataire aurait sil tait dessin.


NSString dispose de mthodes analogues. Avec NSString, nous devons fournir un dictionnaire dattributs qui seront appliqus lintgralit de la chane :
- (void)drawAtPoint:(NSPoint)aPoint withAttributes:(NSDictionary *)attribs

Dessine le destinataire avec les attributs indiqus dans attribs.


- (void)drawInRect:(NSRect)aRect withAttributes:(NSDictionary *)attribs

Dessine le destinataire lintrieur dun rectangle, avec les attributs indiqus dans attribs.
- (NSSize)sizeWithAttributes:(NSDictionary *)attribs

Retourne la taille que le destinataire aurait sil tait dessin avec les attributs indiqus dans attribs.

Afcher les lettres


Ouvrez BigLetterView.h. Ajoutez une variable dinstance qui contiendra le dictionnaire des attributs :
#import <Cocoa/Cocoa.h> @interface BigLetterView: NSView { NSColor *bgColor; NSString *string; NSMutableDictionary *attributes; }

278

Cocoa

Ouvrez BigLetterView.m. Crez une mthode qui gnre le dictionnaire des attributs avec une police et une couleur de premier plan :
- (void)prepareAttributes { attributes = [[NSMutableDictionary alloc] init]; [attributes setObject:[NSFont fontWithName:@"Helvetica" size:75] forKey:NSFontAttributeName]; [attributes setObject:[NSColor redColor] forKey:NSForegroundColorAttributeName]; }

Dans initWithFrame:, nous invoquons cette nouvelle mthode :


- (id)initWithFrame:(NSRect)rect { if (![super initWithFrame:rect]) return nil; NSLog(@"Initialisation de la vue."); [self prepareAttributes]; bgColor = [[NSColor yellowColor] retain]; string = @" "; return self; } - (void)dealloc { [bgColor release]; [string release]; [attributes release]; [super dealloc]; }

Dans la mthode setString:, nous indiquons la vue quelle doit sactualiser :


- (void)setString:(NSString *)c { c = [c copy]; [string release]; string = c; NSLog(@"La chane est prsent %@", string); [self setNeedsDisplay:YES]; }

Crez une mthode qui afchera la chane au milieu dun rectangle :


- (void)drawStringCenteredIn:(NSRect)r { NSSize strSize = [string sizeWithAttributes:attributes]; NSPoint strOrigin; strOrigin.x = r.origin.x + (r.size.width - strSize.width)/2; strOrigin.y = r.origin.y + (r.size.height - strSize.height)/2; [string drawAtPoint:strOrigin withAttributes:attributes]; }

Chapitre 20

Attributs de texte

279

Cette mthode est invoque depuis la mthode drawRect: :


- (void)drawRect:(NSRect)rect { NSRect bounds = [self bounds]; [bgColor set]; [NSBezierPath fillRect:bounds]; [self drawStringCenteredIn:bounds]; if (([[self window] firstResponder] == self) &&

Compilez et excutez lapplication. Vous remarquerez que les vnements du clavier sont traits par la vue sauf lorsquils dclenchent une entre de menu. Essayez dappuyer sur Commande-w : la fentre doit se fermer mme si la vue est le premier rpondeur de la fentre active.

Gnrer des donnes PDF partir de la vue


Toutes les commandes de dessin peuvent tre converties en PDF par le framework AppKit. Les donnes PDF peuvent tre envoyes une imprimante ou enregistres dans un chier. Sachez que le format PDF permet dobtenir la meilleure qualit possible sur nimporte quel priphrique, car il est indpendant de la rsolution. Nous avons dj cr une vue qui sait comment gnrer des donnes PDF pour dcrire son apparence. Placer les donnes PDF dans un chier nest pas trs compliqu, grce la mthode suivante de NSView :
- (NSData *)dataWithPDFInsideRect:(NSRect)aRect

Elle cre un objet de donnes et invoque ensuite drawRect:. Les commandes de dessin normalement destines lcran vont alors tre diriges vers lobjet de donnes. Nous pouvons ensuite simplement enregistrer cet objet de donnes dans un chier. Ouvrez BigLetterView.m et ajoutez une mthode qui cre un panneau Enregistrer sous forme de feuille :
- (IBAction)savePDF:(id)sender { NSSavePanel *panel = [NSSavePanel savePanel]; [panel setRequiredFileType:@"pdf"]; [panel beginSheetForDirectory:nil file:nil modalForWindow:[self window] modalDelegate:self didEndSelector: @selector(didEnd:returnCode:contextInfo:) contextInfo:NULL]; }

Aprs que lutilisateur a choisi le nom du chier, la mthode suivante est invoque :
didEnd:returnCode: contextInfo:

280

Cocoa

Implmentez-la dans BigLetterView.m :


- (void)didEnd:(NSSavePanel *)sheet returnCode:(int)code contextInfo:(void *)contextInfo { if (code!= NSOKButton) return; NSRect r = [self bounds]; NSData *data = [self dataWithPDFInsideRect:r]; NSString *path = [sheet filename]; NSError *error; BOOL successful = [data writeToFile:path options:0 error:&error]; if (!successful) { NSAlert *a = [NSAlert alertWithError:error]; [a runModal]; } }

Dclarez galement laction suivante dans le chier BigLetterView.h :


- (IBAction)savePDF:(id)sender;

Ouvrez le chier nib. Slectionnez Save As dans le menu File. Changez son intitul en Save PDF. Vous pouvez supprimer toutes les autres entres de menu si vous le souhaitez. Associez lentre de menu Save PDF linvocation de la mthode savePDF: de BigLetterView (voir Figure 20.3).
Figure 20.3
Connecter une entre de menu.

Chapitre 20

Attributs de texte

281

Enregistrez le chier nib et compilez lapplication. Vous devez tre en mesure de gnrer un chier PDF et de le visualiser dans Aperu (voir Figure 20.4).
Figure 20.4
Le chier PDF gnr partir de la vue.

Les caractres obtenus par une squence de touche, par exemple "", ne sont pas reconnus par BigLetterView. Pour que cela soit possible, nous devons ajouter plusieurs mthodes utilises par NSInputManager. Ce sujet sort du cadre de ce livre (nous voulions simplement savoir comment rcuprer les vnements du clavier), mais il est trait dans la prsentation Apple de NSInputManager (/Developer/Documentation/ Cocoa/Conceptual/InputManager/index.html).

Pour les plus curieux : NSFontManager


Parfois, la police conviendra parfaitement, mais linterface pourrait tre encore plus jolie si la police tait en gras, en italique ou en condens. NSFontManager permet deffectuer ce type de conversion. Nous pouvons galement utiliser un gestionnaire de polices pour modier la taille. Par exemple, imaginons que nous ayons une police et aimerions en trouver une semblable mais en gras. Voici le code correspondant :
fontManager = [NSFontManager sharedFontManager]; boldFont = [fontManager convertFont:aFont toHaveTrait:NSBoldFontMask];

Exercice 1
Appliquez une ombre la lettre. La classe NSShadow fournit les mthodes suivantes :
- (id)init - (void)setShadowOffset:(NSSize)offset

282

Cocoa

- (void)setShadowBlurRadius:(float)val - (void)setShadowColor:(NSColor *)color

Exercice 2
Ajoutez les variables boolennes bold et italic BigLetterView. Ajoutez des cases cocher qui xent les valeurs de ces variables. Si bold vaut YES, la lettre doit apparatre en gras. Si italic vaut YES, la lettre doit tre afche en italique.

21
Presse-papiers et actions sur nil
Au sommaire de ce chapitre
NSPasteboard Ajouter le couper, copier et coller BigLetterView Actions sur nil Pour les plus curieux : quel objet envoie le message daction ? Pour les plus curieux : copie paresseuse

Le serveur de presse-papiers (/usr/bin/pboard) est un processus qui sexcute en permanence sur le Mac. Les applications utilisent la classe NSPasteboard pour crire et lire des donnes dans ce processus. Le serveur de presse-papiers est lorigine des oprations de copier, couper et coller entre les applications. Une application peut placer dans le presse-papiers des donnes sous plusieurs formats. Par exemple, une image peut tre copie sous forme dun document PDF et dune bitmap. Lapplication qui lit ensuite les donnes choisit le format qui lintresse. Lorsquelle place des donnes dans le presse-papiers, lapplication dclare gnralement les diffrents types de ces donnes et les copie immdiatement. Lapplication qui souhaite lire les donnes commence par demander au presse-papiers les types disponibles, puis elle lit les donnes dans son format prfr. Il est galement possible de copier des donnes dans le presse-papiers de manire paresseuse. Pour cela, il suft de dclarer tous les types que lapplication peut placer dans le presse-papiers, puis de fournir des donnes lorsquelles sont demandes. Nous reviendrons sur la copie paresseuse la n de ce chapitre. Il existe plusieurs presse-papiers. Lun est destin aux oprations de copier-coller, un autre, aux oprations de glisser-dposer. Un presse-papiers conserve la dernire chane

284

Cocoa

recherche par lutilisateur. Un autre sert la copie des rgles ; un autre encore, la copie des polices. Dans cette section, nous ajouterons des possibilits de couper, copier et coller BigLetterView. Tout dabord, nous implmenterons les mthodes qui lisent et crivent le presse-papiers. Ensuite, nous verrons comment les appeler.

NSPasteboard
La classe NSPasteboard sert dinterface avec le serveur de presse-papiers. Voici les mthodes de NSPasteboard les plus utilises :
+ (NSPasteboard *)generalPasteboard

Retourne le NSPasteboard gnral. Nous utiliserons ce presse-papiers pour les fonctions de copier, couper et coller.
+ (NSPasteboard *)pasteboardWithName:(NSString *)name

Retourne le presse-papiers identi par name. Voici les variables globales qui correspondent aux presse-papiers standard :
NSGeneralPboard NSFontPboard NSRulerPboard NSFindPboard NSDragPboard

- (int)declareTypes:(NSArray *)types owner:(id)theOwner

Efface le contenu du presse-papiers et dclare les types de donnes que theOwner y placera. Voici les variables globales qui correspondent aux types standard :
NSColorPboardType NSFileContentsPboardType NSFilenamesPboardType NSFontPboardType NSPDFPboardType NSPICTPboardType NSPostScriptPboardType NSRulerPboardType NSRTFPboardType NSRTFDPboardType NSStringPboardType NSTabularTextPboardType NSVCardPboardType NSTIFFPboardType NSURLPboardType

Chapitre 21

Presse-papiers et actions sur nil

285

Il est galement possible de crer ses propres types pour le presse-papiers.


- (BOOL)setData:(NSData *)aData forType:(NSString *)dataType - (BOOL)setString:(NSString *)s forType:(NSString *)dataType

crit des donnes dans le presse-papiers.


- (NSArray *)types

Retourne un tableau qui contient les types des donnes disponibles en lecture dans le presse-papiers.
- (NSString *)availableTypeFromArray:(NSArray *)types

Retourne le premier type, parmi ceux indiqus dans types, trouv dans le pressepapiers. La liste types identie tous les types que nous sommes capables de lire.
- (NSData *)dataForType:(NSString *)dataType - (NSString *)stringForType:(NSString *)dataType

Lisent des donnes depuis le presse-papiers.

Ajouter le couper, copier et coller BigLetterView


Nous allons crer les mthodes cut:, copy: et paste: lors la classe BigLetterView. Pour quelles soient plus faciles crire, nous commenons par crer des mthodes pour crire des donnes dans un presse-papiers et les lire. Ajouter les mthodes suivantes dans BigLetterView.m :
- (void)writeToPasteboard:(NSPasteboard *)pb { // Dclarer les types. [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self]; // Copier des donnes dans le presse-papiers. [pb setString:string forType:NSStringPboardType]; } - (BOOL)readFromPasteboard:(NSPasteboard *)pb { // Le presse-papiers contient-il une chane? NSArray *types = [pb types]; if ([types containsObject:NSStringPboardType]) { // Lire la chane depuis le presse-papiers. NSString *value = [pb stringForType:NSStringPboardType];

286

Cocoa

// Notre vue gre une seule lettre. if ([value length] == 1) { [self setString:value]; return YES; } } return NO; }

Ajoutez cut:, copy: et paste: dans BigLetterView.m :


- (IBAction)cut:(id)sender { [self copy:sender]; [self setString:@""]; } - (IBAction)copy:(id)sender { NSPasteboard *pb = [NSPasteboard generalPasteboard]; [self writeToPasteboard:pb]; } - (IBAction)paste:(id)sender { NSPasteboard *pb = [NSPasteboard generalPasteboard]; if(![self readFromPasteboard:pb]) { NSBeep(); } }

Dclarez ces mthodes dans BigLetterView.h :


- (IBAction)cut:(id)sender - (IBAction)copy:(id)sender - (IBAction)paste:(id)sender

Actions sur nil


Puisqu il existe un trs grand nombre de vues, comment un message cut:, copy: ou paste: est-il envoy la bonne vue ? Si nous slectionnons un champ de texte, il reoit le message. Lorsque nous slectionnons une autre vue et choisissons lentre de menu Copier ou Coller, le message doit aller la vue slectionne. Pour rsoudre ce problme, les brillants ingnieurs de NeXT ont invent les actions sur nil. Lorsque la cible dun contrle est nil, lapplication tente denvoyer le message plusieurs objets jusqu ce que lun deux rponde. Elle commence par cibler le premier rpondeur de la fentre active. Il sagit prcisment du comportement souhait pour

Chapitre 21

Presse-papiers et actions sur nil

287

Couper et Coller. Nous pouvons avoir plusieurs fentres, chacune avec plusieurs vues. La vue active dans la fentre active reoit les messages couper-coller.

Lintrt des actions cibles va plus loin. NSView, NSApplication et NSWindow drivent toutes de NSResponder, qui possde une variable dinstance nomme nextResponder. Si un objet ne rpond pas une action sur nil, une chance est alors offerte son nextResponder. Le nextResponder dune vue est gnralement la vue suprieure. Le nextResponder de la vue contenu dune fentre est la fentre. Ainsi, les rpondeurs sont lis les uns aux autres et forment la chane des rpondeurs.
nextResponder na aucun rapport avec nextKeyView. Par exemple, une entre de menu ferme la fentre active. Sa cible est nil. Laction est performClose:. Aucun des objets standard ne rpond performClose:, lexception de NSWindow. Ainsi, le champ de texte slectionn, par exemple, refuse de rpondre performClose:. La vue suprieure du champ de texte refuse galement, et le message remonte la hirarchie de vues. La fentre (active) nit par accepter la mthode performClose:. Pour lutilisateur, la fentre "active" est donc ferme.

Nous lavons mentionn au Chapitre 12, un panneau peut devenir la fentre active, mais pas la fentre principale. Si la fentre active et la fentre principale diffrent, toutes deux ont lopportunit de rpondre une action sur nil. Analyser la chane des rpondeurs Dans quel ordre seront tests les objets avant quune action sur nil soit annule ? 1. Le premier rpondeur de la fentre active (keyWindow) et sa chane des rpondeurs. La chane des rpondeurs comprend les vues suprieures et la fentre active. 2. Le dlgu (delegate) de la fentre active. 3. Sil sagit dune application base de documents, le NSWindowController puis le NSDocument de la fentre active. 4. Si la fentre principale diffre de la fentre active, le test se fait de la mme manire pour la fentre principale :
Le premier rpondeur de la fentre principale et sa chane des rpondeurs, y compris la fentre principale elle-mme. Le dlgu de la fentre principale. Les objets NSWindowController et NSDocument de la fentre principale.

5. Linstance de NSApplication.

288

Cocoa

6. Le dlgu de NSApplication. 7. Le NSDocumentController. Cette squence dobjet correspond la chane des rpondeurs. La Figure 21.1 prsente un exemple. Les nombres indiquent lordre dans lequel les objets sont interrogs pour savoir sils rpondent aux actions sur nil.

MaClasse

Figure 21.1
Exemple dordre dans lequel les rpondeurs ont lopportunit de rpondre.

Dans une application base sur des documents, comme RaiseMan, lobjet NSDocument a loccasion de rpondre laction sur nil. Il reoit les messages partir des entres de menu suivantes : Enregistrer, Enregistrer sous, Revenir la version enregistre, Imprimer et Format dimpression. Examiner le chier nib Ouvrez le chier nib. Vous remarquerez que les lments couper, copier et coller sont connects licne intitule First Responder. Cette icne reprsente nil et constitue une cible lorsque nous devons faire glisser un objet dont la cible doit tre nil (voir Figure 21.2).

Chapitre 21

Presse-papiers et actions sur nil

289

Figure 21.2
Vrier une entre de menu.

Les actions qui apparaissent alors dans linspecteur se trouvent dans linspecteur Identity sous Class Actions. Il suft dajouter une action cette liste pour quelle apparaisse lorsquun objet est dpos sur First Responder. Compilez et excutez lapplication. Les fonctions de couper, copier et coller fonctionnent parfaitement avec la vue. Les quivalents claviers sont galement disponibles. Vous ne pouvez copier dans BigLetterView que des chanes dun caractre.

Pour les plus curieux : quel objet envoie le message daction ?


La cible des entres de menu couper, copier et coller est nil. Nous savons que lenvoi dun message nil na aucun effet. En ralit, tous les messages cible/action sont grs par NSApplication. Elle fournit la mthode suivante :
- (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)sender

Lorsque la cible est nil, NSApplication sait quil doit essayer denvoyer des messages des objets de la chane des rpondeurs.

Pour les plus curieux : copie paresseuse


Une application peut implmenter la copie dans un presse-papiers de manire paresseuse. Par exemple, imaginons une application graphique qui copie des images volumineuses dans le presse-papiers sous plusieurs formats : PICT, TIFF, PDF, etc. Il est facile de comprendre que la copie de tous ces formats soumettra rude preuve lapplication et le serveur de presse-papiers. Pour un fonctionnement plus lger, lapplication peut effectuer la place une copie paresseuse. Autrement dit, elle dclare tous les types quelle est capable de placer dans le presse-papiers, mais elle copie les donnes uniquement lorsquune autre application les demande.

290

Cocoa

Lapplication place un IOU (au lieu des donnes) dans le presse-papiers et dsigne un objet qui fournira des donnes lorsquelles seront requises. Lorsquune autre application souhaite lire les donnes, le serveur de presse-papiers rappelle cet objet pour les obtenir. La dclaration fonctionne comme prcdemment :
- (int)declareTypes:(NSArray *)types owner:(id)theOwner

En revanche, theOwner doit implmenter la mthode suivante :


- (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type

Lorsquune autre application a besoin des donnes, cette mthode est invoque. ce moment-l, lapplication doit copier les donnes promises dans le presse-papiers indiqu. Vous limaginez sans mal, un problme se pose si le serveur de presse-papiers demande les donnes alors que lapplication est termine. Lorsquune application en cours de fermeture dispose dun IOU dans le presse-papiers, il lui est demand de fournir toutes les donnes promises avant de se terminer. Ainsi, il nest pas rare que le propritaire dun IOU reoive plusieurs fois le message pasteboard:provideDataForType: pendant que lapplication est en cours de fermeture. La copie paresseuse prsente une autre difcult lorsque les donnes sont copies dans le presse-papiers et, plus tard, colles dans une autre application. Lutilisateur qui invoque la fonction coller ne souhaite pas obtenir ltat le plus rcent des donnes, mais ltat dans lequel elles se trouvaient au moment o elles ont t copies. Lorsquils implmentent une copie paresseuse, la plupart des dveloppeurs prennent une forme de clich des informations au moment de la dclaration des types. Pour fournir les donnes, le clich est copi dans le presse-papiers la place de ltat courant des donnes. Bien entendu, lorsque lutilisateur effectue une copie ailleurs, lobjet nest plus responsable de la conservation du clich.
- (void)pasteboardChangedOwner:(NSPasteboard *)sender

Si cette mthode est implmente, elle est invoque lorsque lobjet nest plus responsable de la conservation du clich.

Chapitre 21

Presse-papiers et actions sur nil

291

Exercice 1
La chane est place dans le presse-papiers. Crez le PDF de la vue et placez-le galement dans le presse-papiers. Il sera ainsi possible de copier limage de la lettre dans des applications graphiques. Testez cette capacit en utilisant lentre de menu Crer partir du presse-papiers de lapplication Aperu. Ne dtruisez pas le copier-coller de la chane. Placez la fois la chane et le PDF dans le presse-papiers.

Exercice 2
Dans le projet RaiseMan, ajoutez une entre de menu qui dclenche linvocation de la mthode removeEmployee: dans MyDocument.

22
Catgories
Au sommaire de ce chapitre
Ajouter une mthode NSString Pour les plus curieux : dclarer des mthodes prives Pour les plus curieux : dclarer des protocoles informels

Les ingnieurs dApple ont beau tre futs, il nous arrive parfois de penser que "si seulement ils avaient pu placer cette mthode dans cette classe, ma vie serait beaucoup plus simple". Lorsque cela se produit, nous pouvons crer une catgorie, cest-dire un ensemble de mthodes que nous aimerions voir ajoutes une classe existante. Le concept de catgorie est trs utile, et il est surprenant quaussi peu de langages orients objet mettent en uvre cette ide brillante. Il est plus facile de crer des catgories que den parler. Au chapitre prcdent, nous avons ajout des fonctions de copier-coller BigLetterView. Cependant, si la chane dans le presse-papiers contient plusieurs lettres, le copier choue car BigLetterView ne peut pas afcher plus dune lettre la fois. tendons lexemple an de prendre uniquement la premire lettre de la chane au lieu dchouer.

Ajouter une mthode NSString


Nous souhaiterions que chaque objet NSString offre une mthode qui retourne sa premire lettre. Puisque ce nest pas le cas, nous allons utiliser une catgorie pour ajouter cette mthode. Ouvrez le projet et crez un nouveau chier de type Objective-C class. En ralit, nous ne voulons pas crer une classe mais elle va nous servir de point de dpart pour la cration de la catgorie. Nommez ce chier FirstLetter.m ; crez galement FirstLetter.h.

294

Cocoa

Modiez FirstLetter.h an de dclarer la catgorie :


#import <Foundation/Foundation.h> @interface NSString (FirstLetter) - (NSString *)BNR_firstLetter; @end

Nous semblons dclarer la classe NSString, mais sans lui donner de variables dinstance ou de superclasse. En ralit, nous nommons la catgorie FirstLetter et dclarons une mthode. Une catgorie ne peut pas ajouter de variables dinstance la classe, uniquement des mthodes. Implmentez ensuite la mthode BNR_firstLetter dans le chier FirstLetter.m :
#import "FirstLetter.h" @implementation NSString (FirstLetter) - (NSString *)BNR_firstLetter { if ([self length] < 2) { return self; } NSRange r; r.location = 0; r.length = 1; return [self substringWithRange:r]; } @end

Nous pouvons ensuite utiliser cette mthode comme si elle faisait partie de NSString. Dans BigLetterView.m, modiez readFromPasteboard: de la manire suivante :
- (BOOL)readFromPasteboard:(NSPasteboard *)pb { // Le presse-papiers contient-il une chane? NSArray *types = [pb types]; if ([types containsObject:NSStringPboardType]) { // Lire la chane depuis le presse-papiers. NSString *value = [pb stringForType:NSStringPboardType]; [self setString:[value BNR_firstLetter]]; return YES; } return NO; }

Chapitre 22

Catgories

295

Au dbut de BigLetterView.m, ajoutez limportation de len-tte :


#import "FirstLetter.h"

Compilez et excutez lapplication. Vous pouvez copier des chanes de caractres contenant plusieurs lettres dans BigLetterView. Seule la premire lettre de la chane est copie. Dans cet exemple, nous avons ajout une seule mthode, mais rien ne nous empche den ajouter plusieurs. Par ailleurs, nous navons utilis que les mthodes de la classe, mais nous pouvons galement accder directement ses variables dinstance. Nous avons employ le prxe BNR_ dans le nom de la mthode de notre catgorie. Nous aurions bien voulu nommer cette mthode firstLetter, mais que se passerait-il si Apple ajoutait une mthode firstLetter NSString dans Mac OS X 10.9 ? Ces deux mthodes entreraient en conit. Il est donc prfrable dajouter un prxe. Cocoa dnit de nombreuses catgories. Par exemple, NSAttributedString fait partie du framework Foundation. Cependant, la mthode drawInRect: de NSAttributedString fait partie dune catgorie fournie par le framework AppKit. Cest pourquoi la documentation des mthodes de NSAttributedString est rpartie sur les deux frameworks. Il existe galement des chiers den-tte spars pour NSAttributedString et ses catgories. Tout cela contribue une certaine confusion.

Pour les plus curieux : dclarer des mthodes prives


Parfois, nous ne souhaitons pas que certaines mthodes dnies dans un chier .m soient annonces publiquement par une dclaration dans le chier .h correspondant. Il sagit alors de mthodes prives. Si nous appelons une mthode prive avant de la dclarer ou de la dnir, nous recevons un avertissement de la part du compilateur. Pour viter ces avertissements, une technique classique consiste dclarer les mthodes prives dans une catgorie au dbut du chier .m :
#import "Megatron.h" // Dclarer les mthodes prives. @interface Megatron () - (void)blowTheLidOff; - (void)putTheLidBackOn; @end @implementation Megatron ... Implmenter toutes les mthodes prives et publiques... @end

296

Cocoa

Pour les plus curieux : dclarer des protocoles informels


Avec Objective-C 2.0, nous pouvons marquer certaines parties dun protocole avec @optional, et cest ainsi que nous dclarons des mthodes dlgues dans ce langage. Mais ce besoin existait dj avant larrive dObjective-C 2.0. La mthode employe alors tait laide et appele protocole informel. En examinant la n de NSWindow.h, nous trouvons le code suivant :
@interface NSObject (NSWindow) - (BOOL)windowShouldClose:(NSWindow *)w; - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)aSize; ... @end

Il sagit non pas rellement dune catgorie sur NSObject, mais de la manire affreuse servant dclarer des mthodes dlgues.

23
Glisser-dposer
Au sommaire de ce chapitre
BigLetterView en tant que source BigLetterView en tant que destination Pour les plus curieux : masque des oprations

Le glisser-dposer nest pas quune simple opration copier-coller plus tapageuse. Au dbut de lopration, des donnes sont copies dans le presse-papiers rserv au glisserdposer. Lorsque le bouton de la souris est relch, les donnes sont lues partir de ce presse-papiers. Cette technique est plus complexe que le copier-coller en raison du retour prsent aux utilisateurs : une image qui safche pendant le dplacement de la souris, une vue mise en exergue lorsque le pointeur la survole et, peut-tre, un bruit de dglutition lorsque limage est dpose. Lorsquon fait glisser des donnes dune application vers une autre, plusieurs choses peuvent se produire : rien ne se passe, une copie des donnes est cre ou un lien vers les donnes existantes est cr. Voici les constantes qui reprsentent ces trois oprations :
NSDragOperationNone NSDragOperationCopy NSDragOperationLink

Il existe dautres oprations, mais moins frquentes :


NSDragOperationGeneric NSDragOperationPrivate NSDragOperationMove NSDragOperationDelete NSDragOperationEvery

298

Cocoa

La source et la destination doivent saccorder sur lopration ralise lorsque lutilisateur relche le bouton de la souris. Pour ajouter le glisser-dposer une vue, les modications sont de deux ordres : 1. En faire une source du glisser-dposer. 2. En faire une destination du glisser-dposer. Examinons sparment chaque tape. Tout dabord, nous allons faire de notre vue une source du glisser-dposer. Lorsque cet aspect sera oprationnel, nous en ferons une destination du glisser-dposer.

BigLetterView en tant que source


Lorsque cette section sera termine, nous serons capables de faire glisser une lettre depuis BigLetterView et de la dposer dans nimporte quel diteur de texte, comme lillustre la Figure 23.1.
Figure 23.1
Lapplication termine.

Pour constituer une source, notre vue doit implmenter draggingSourceOperationMaskForLocal:. Cette mthode dclare les oprations auxquelles la vue accepte de participer en tant que source. Ajoutez la mthode suivante dans BigLetterView.m:
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal { return NSDragOperationCopy; }

Cette mthode est appele automatiquement deux fois : une premire avec isLocal YES, pour dterminer les oprations auxquelles la vue accepte de participer lorsque les destinations proviennent de notre application, et une seconde avec isLocal NO, pour

Chapitre 23

Glisser-dposer

299

dterminer des oprations auxquelles elle accepte de participer pour les destinations qui proviennent dautres applications. Pour dbuter une opration de glisser-dposer, nous utiliserons une mthode de NSView :
- (void)dragImage:(NSImage *)anImage at:(NSPoint)imageLoc offset:(NSSize)mouseOffset event:(NSEvent *)theEvent pasteboard:(NSPasteboard *)pboard source:(id)sourceObject slideBack:(BOOL)slideBack

Nous lui indiquerons limage concerne par lopration et la position de dbut de cette opration. Lvnement indiqu doit tre mouseDown. Le dcalage est totalement ignor. Le presse-papiers est gnralement celui rserv au glisser-dposer. Lorsque le dposer ne se fait pas, nous pouvons prciser si licne de lanimation doit revenir son emplacement dorigine. Ajoutez une variable dinstance dans BigLetterView.h pour stocker lvnement mouseDown :
NSEvent *mouseDownEvent;

Dans mouseDown:, nous mmorisons lvnement dans cette variable dinstance. Modiez la mthode correspondante dans BigLetterView.m :
- (void)mouseDown:(NSEvent *)event { [event retain]; [mouseDownEvent release]; mouseDownEvent = event; }

Nous devons galement crer limage faire glisser. Il est possible de dessiner sur une image de la mme manire que sur une vue. Pour que le dessin apparaisse sur limage et non sur lcran, nous devons tout dabord verrouiller le focus sur limage. Lorsque le dessin est termin, nous devons librer le focus. Voici lintgralit de la mthode que vous devez ajouter dans BigLetterView.m :
- (void)mouseDragged:(NSEvent *)event { NSPoint down = [mouseDownEvent locationInWindow]; NSPoint drag = [event locationInWindow]; float distance = hypot(down.x - drag.x, down.y - drag.y); if (distance < 3) { return; } // La chane est-elle vide? if ([string length] == 0) {

300

Cocoa

return; } // Obtenir la taille de la chane. NSSize s = [string sizeWithAttributes:attributes]; // Crer limage qui sera dplace. NSImage *anImage = [[NSImage alloc] initWithSize:s]; // Crer un rectangle dans lequel sera dessine la lettre // sur limage. NSRect imageBounds; imageBounds.origin = NSZeroPoint; imageBounds.size = s; // Dessiner la lettre sur limage. [anImage lockFocus]; [self drawStringCenteredIn:imageBounds]; [anImage unlockFocus]; // Obtenir lemplacement de lvnement mouseDown. NSPoint p = [self convertPoint:down fromView:nil]; // Faire glisser le centre de limage. p.x = p.x - s.width/2; p.y = p.y - s.height/2; // Obtenir le presse-papiers. NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSDragPboard]; // Placer la chane dans le presse-papiers. [self writeToPasteboard:pb]; // Dbuter lopration de glisser. [self dragImage:anImage at:p offset:NSMakeSize(0, 0) event:mouseDownEvent pasteboard:pb source:self slideBack:YES]; [anImage release]; }

Et voil ! Compilez et excutez lapplication. Vous devez tre en mesure de faire glisser une lettre hors de la vue et de la dposer dans nimporte quel diteur de texte. Essayez avec Xcode. La source est avertie du dposer si elle implmente la mthode suivante :
- (void)draggedImage:(NSImage *)image endedAt:(NSPoint)screenPoint operation:(NSDragOperation)operation;

Chapitre 23

Glisser-dposer

301

Par exemple, pour effacer BigLetterView lorsque la lettre est dpose dans la corbeille du Dock, nous devons annoncer notre bonne volont dans draggingSource-OperationMaskForLocal: :
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal { return NSDragOperationCopy | NSDragOperationDelete; }

Il suft ensuite dimplmenter draggedImage:endedAt:operation: :


- (void)draggedImage:(NSImage *)image endedAt:(NSPoint)screenPoint operation:(NSDragOperation)operation { if (operation == NSDragOperationDelete) { [self setString:@""]; } }

Compilez et excutez lapplication. Faites glisser une lettre dans la corbeille. La lettre doit disparatre de la vue.

BigLetterView en tant que destination


Devenir la destination dune opration de glisser-dposer implique plusieurs tapes. Tout dabord, nous devons dclarer que la vue est une destination pour certains types doprations. Pour cela, NSView dispose de la mthode suivante :
- (void)registerForDraggedTypes:(NSArray *)pboardTypes

Elle est gnralement invoque dans la mthode initWithFrame:. Nous devons galement implmenter six mthodes. Elles prennent toutes le mme argument : un objet NSDraggingInfo qui dsigne le presse-papiers utilis pour le glisserdposer. Les six mthodes sont invoques comme suit : 1. Lorsque limage arrive sur la destination, celle-ci reoit un message draggingEntered:. En gnral, la destination change daspect. Par exemple, elle peut se surligner. 2. Pendant que limage reste dans la destination, des messages draggingUpdated: sont envoys. Limplmentation de draggingUpdated: est facultative. 3. Si limage est dplace hors de la destination, celle-ci reoit draggingExited:. 4. Si limage est dpose sur la destination, soit elle revient vers sa source et interrompt la squence, soit un message prepareForDragOperation: est envoy la destination, selon la valeur retourne par la dernire invocation de draggingEntered: (ou draggingUpdated: si cette mthode est implmente).

302

Cocoa

5. Si le message prepareForDragOperation: retourne YES, un message performDragOperation: est envoy. En gnral, cest ce moment-l que lapplication lit les donnes prsentes dans le presse-papiers. 6. Enn, si performDragOperation: a retourn YES, concludeDragOperation: est envoy. Lapparence peut changer. Cest ce moment-l que nous pouvons mettre le son de dglutition pour indiquer le succs du dposer. registerForDraggedTypes Nous ajoutons un appel registerForDraggedTypes: depuis la mthode initWithFrame: dans BigLetterView.m :
- (id)initWithFrame:(NSRect)rect { [super initWithFrame:rect]; NSLog(@"Initialisation de la vue."); [self prepareAttributes]; bgColor = [[NSColor yellowColor] retain]; string = @""; [self registerForDraggedTypes: [NSArray arrayWithObject:NSStringPboardType]]; return self; }

Ajouter la mise en exergue Pour indiquer lutilisateur que lopration dposer est accepte, notre vue se met en exergue. Ajoutez la variable dinstance highlighted dans BigLetterView.h :
@interface BigLetterView: NSView { NSColor *bgColor; NSString *string; NSMutableDictionary *attributes; NSEvent *mouseDownEvent; BOOL highlighted; } ...

Nous allons prsent ajouter le code de mise en exergue dans drawRect:. Grce la classe NSGradient, il est trs facile de dessiner des dgrads. Dans notre exemple, nous appliquerons un dgrad radial blanc au centre, pour aller vers bgColor :
- (void)drawRect:(NSRect)rect { NSRect bounds = [self bounds]; // Dessiner un arrire-plan dgrad si la vue est mise en exergue. if (highlighted) { NSGradient *gr;

Chapitre 23

Glisser-dposer

303

gr = [[NSGradient alloc] initWithStartingColor:[NSColor whiteColor] endingColor:bgColor]; [gr drawInRect:bounds relativeCenterPosition:NSZeroPoint]; [gr release]; } else { [bgColor set]; [NSBezierPath fillRect:bounds]; } [self drawStringCenteredIn:bounds]; ...

Implmenter les mthodes dune destination Jusqu prsent, nous avons vu deux manires de dclarer un pointeur sur un objet. Si le pointeur peut faire rfrence nimporte quel type dobjet, il est dclar ainsi :
id toto;

Si le pointeur doit faire rfrence une instance dune certaine classe, nous le dclarons comme suit :
MaClasse *toto;

Il existe galement une troisime possibilit. Si le pointeur doit faire rfrence un objet qui se conforme un certain protocole, nous le dclarons de la manire suivante :
id <MonProtocole> toto;

NSDraggingInfo est un protocole, non une classe. Toutes les mthodes dune destination dune opration de glisser-dposer attendent un objet qui respecte le protocole NSDraggingInfo.

Ajoutez les mthodes suivantes dans BigLetterView.m :


#pragma mark Dragging Destination - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender { NSLog(@"draggingEntered:"); if ([sender draggingSource] == self) { return NSDragOperationNone; } highlighted = YES; [self setNeedsDisplay:YES]; return NSDragOperationCopy; } - (void)draggingExited:(id <NSDraggingInfo>)sender { NSLog(@"draggingExited:"); highlighted = NO; [self setNeedsDisplay:YES]; }

304

Cocoa

- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender { return YES; } - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender { NSPasteboard *pb = [sender draggingPasteboard]; if(![self readFromPasteboard:pb]) { NSLog(@"Erreur: lecture impossible depuis le presse-papiers du glisser-dposer "); return NO; } return YES; } - (void)concludeDragOperation:(id <NSDraggingInfo>)sender { NSLog(@"concludeDragOperation:"); highlighted = NO; [self setNeedsDisplay:YES]; }

Tester Ouvrez le chier nib et ajoutez un autre BigLetterView dans la fentre. Supprimez les champs de texte. Vriez que la variable nextKeyView de chaque BigLetterView permet de passer dune vue lautre avec la touche Tab (voir Figure 23.2).
Figure 23.2
Fixer nextKeyView pour chaque BigLetterView.

Compilez et excutez lapplication. Vous pouvez faire glisser des caractres entre les vues et depuis dautres applications.

Chapitre 23

Glisser-dposer

305

Pour les plus curieux : masque des oprations


Pour certaines applications, la ngociation des oprations qui se produiront lorsque lutilisateur relchera le bouton de la souris peut se rvler assez complexe. En effet, la source signale quelle accepte de participer certaines oprations par lintermdiaire de draggingSourceOperationMaskForLocal:. Lutilisateur peut galement indiquer ses prfrences en maintenant enfonce la touche Contrle, Option ou Commande. Cest la destination de dterminer ce qui va se passer. Lobjet dinformations sur lopration prend en charge une bonne partie du travail. Il obtient le masque des oprations dni par la source et le ltre en fonction des touches de modication enfonces par lutilisateur. Pour voir ce fonctionnement luvre, nous implmentons draggingUpdated: et consignons le masque des oprations contenu dans lobjet dinformations :
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender { NSDragOperation op = [sender draggingSourceOperationMask]; NSLog(@"Masque dopration = %d", op); if ([sender draggingSource] == self) { return NSDragOperationNone; } return NSDragOperationCopy; }

Compilez et excutez lapplication. Faites glisser du texte provenant de diffrentes sources en maintenant enfonces certaines touches de modication. Notez le comportement du masque et du pointeur.

24
NSTimer
Au sommaire de ce chapitre
Agencer linterface tablir les connexions Implmenter AppController Pour les plus curieux : NSRunLoop

Une instance de NSButton possde une cible et une action (slecteur). Suite au clic sur le bouton, le message daction est envoy la cible. Les minuteries fonctionnent de manire semblable. Une minuterie est un objet qui possde une cible, un slecteur et une temporisation dont la dure est donne en secondes (voir Figure 24.1).
Figure 24.1
NSTimer.

Une fois la temporisation coule, le message du slecteur est envoy la cible. La minuterie est incluse en argument du message. Elle peut galement tre congure de manire rpter le message.

308

Cocoa

Nous allons jouer avec les minuteries en crant une application dapprentissage de la saisie. Elle contiendra deux objets BigLetterView, le premier afchant ce que lutilisateur doit saisir, le second, ce que lutilisateur a saisi (voir Figure 24.2).
Figure 24.2
Lapplication termine.

Un NSProgressIndicator afchera le temps restant. Aprs deux secondes, lapplication mettra un bip pour indiquer lutilisateur quil est trop lent. Il lui sera alors accord deux secondes supplmentaires. Nous allons crer une classe AppController. Lorsque lutilisateur cliquera sur le bouton Dmarrer, une instance de NSTimer sera cre. La minuterie enverra un message toutes les 0,2 seconde. La mthode dclenche vriera si les deux vues correspondent. Dans lafrmative, la saisie dune nouvelle lettre sera propose lutilisateur. Dans le cas contraire, lindicateur de progression sera incrment. Si lutilisateur suspend lapplication, la minuterie est arrte. La Figure 24.3 prsente le graphe dobjets.

Figure 24.3
Graphe dobjets.

Chapitre 24

NSTimer

309

Revenez au projet TypingTutor dans Xcode. Crez une nouvelle classe Objective-C nomme AppController. Dans AppController.h, nous dclarons deux outlets et une action. Nous aurons galement besoin dune minuterie, dun tableau pour les lettres, de lindice de la dernire lettre afche et dun compteur indiquant la dure dafchage de la lettre actuelle :
#import <Cocoa/Cocoa.h> @class BigLetterView; @interface AppController: NSObject { // Outlets. IBOutlet BigLetterView *inLetterView; IBOutlet BigLetterView *outLetterView; // Donnes. NSArray *letters; int lastIndex; // Minuterie. NSTimer *timer; int count; } - (IBAction)stopGo:(id)sender; - (void)incrementCount; - (void)resetCount; - (void)showAnotherLetter; @end

Agencer linterface
Ouvrez MainMenu.nib. Crez une instance dAppController en faisant glisser un Object (sous Objects & Controllers) dans la fentre du chier nib. Fixez sa classe AppController (voir Figure 24.4).
Figure 24.4
Crer une instance dAppController.

310

Cocoa

Slectionnez le BigLetterView de gauche. Dans le menu Layout, choisissez Embed Objects in > Box. Intitulez la bote Taper ici. Placez lautre BigLetterView dans une bote intitule Ceci. Dposez un NSProgressIndicator sur la fentre. Utilisez linspecteur pour quil ne soit pas indtermin. Fixez sa plage 0100 (voir Figure 24.5).

Figure 24.5
Inspecter lindicateur de progression.

Placez un bouton sur la fentre. laide de linspecteur, intitulez-le Dmarrer, avec le texte alternatif Pause. Choisissez le style de bouton Round Textured et le mode Toggle. Dcochez galement la case Selected (voir Figure 24.6).
Figure 24.6
Inspecter le bouton.

Chapitre 24

NSTimer

311

tablir les connexions


En maintenant la touche Contrle enfonce, faites glisser le bouton vers lobjet AppController. Fixez laction a stopGo: (voir Figure 24.7).

Figure 24.7
Connectez le bouton lAppController.

Liez la valeur du NSProgressIndicator lattribut count de lAppController (voir Figure 24.8).

Figure 24.8
Lier lindicateur de progression lAppController.

312

Cocoa

Faites un Contrle-clic sur lAppController pour afcher le panneau des connexions. Reliez inLetterView au BigLetterView de gauche et outLetterView au BigLetterView de droite (voir Figure 24.9).
Figure 24.9
Connecter loutlet outLetterView.

Implmenter AppController
Ajoutez les mthodes suivantes dans AppController.m:
#import "AppController.h" #import "BigLetterView.h" #define MAX_COUNT (100) #define COUNT_STEP (5) @implementation AppController - (id)init { [super init]; // Crer un tableau de lettres. letters = [[NSArray alloc] initWithObjects:@"a", @"s", @"d",@"f", @"j", @"k", @"l", @";",nil]; // Initialiser le gnrateur de nombres alatoires. srandom(time(NULL)); return self; }

Chapitre 24

NSTimer

313

- (void)awakeFromNib { [self showAnotherLetter]; } - (void)resetCount { [self willChangeValueForKey:@"count"]; count = 0; [self didChangeValueForKey:@"count"]; } - (void)incrementCount { [self willChangeValueForKey:@"count"]; count = count + COUNT_STEP; if (count > MAX_COUNT) { count = MAX_COUNT; } [self didChangeValueForKey:@"count"]; } - (void)showAnotherLetter { // Choisir des nombres alatoires jusqu obtenir une valeur // diffrente du dernier. int x = lastIndex; while (x == lastIndex){ x = random() % [letters count]; } lastIndex = x; [outLetterView setString:[letters objectAtIndex:x]]; // Recommencer le dcompte. [self resetCount]; } - (IBAction)stopGo:(id)sender { if (timer == nil) { NSLog(@"Dmarrer"); // Crer une minuterie. timer = [[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(checkThem:) userInfo:nil repeats:YES] retain]; } else { NSLog(@"Arrter"); // Invalider et librer la minuterie. [timer invalidate]; [timer release]; timer = nil; } }

314

Cocoa

- (void)checkThem:(NSTimer *)aTimer { if ([[inLetterView string] isEqual:[outLetterView string]]) { [self showAnotherLetter]; } if (count == MAX_COUNT) { NSBeep(); [self resetCount]; } else { [self incrementCount]; } } @end

Compilez et excutez lapplication. Notez, une fois encore, que nous avons spar nos classes en vues ( BigLetterView) et en contrleurs (AppController). Si nous dveloppions une application complte, nous ajouterions probablement des classes modles, comme Lecon et Etudiant.

Pour les plus curieux : NSRunLoop


NSRunLoop est un objet qui attend. Il attend que des vnements arrivent, pour ensuite les transmettre un NSApplication. Il attend que des vnements de minuterie arrivent, pour ensuite les transmettre un NSTimer. Il est mme possible dattacher une socket rseau la boucle dattente pour attendre que des donnes arrivent sur cette socket.

Exercice
Modiez lapplication ImageFun pour que le dlement automatique soit pilot par une minuterie. Supprimez la mthode mouseDragged: de StretchView. Dans mouseDown:, crez une minuterie rptitive qui invoque une mthode de la vue un intervalle de 1/10 de seconde. Dans la mthode invoque, effectuez un dlement automatique en utilisant lvnement courant. Pour obtenir cet vnement, invoquez la mthode currentEvent de NSApplication :
NSEvent *e = [NSApp currentEvent];

Noubliez pas que NSApp est une variable globale qui pointe sur linstance de NSApplication. Invalidez et librez la minuterie dans mouseUp:. Vous remarquerez que lautodlement est beaucoup plus harmonieux et prvisible.

25
Feuilles
Au sommaire de ce chapitre
Ajouter une feuille Pour les plus curieux : contextInfo Pour les plus curieux : fentres modales

Une feuille nest rien dautre quune instance de NSWindow attache une autre fentre. La feuille safche par-dessus la fentre et celle-ci ne reoit plus les vnements tant que la feuille nest pas ferme. En gnral, une feuille est cre en tant que fentre hors cran dans le chier nib. Plusieurs mthodes de NSApplication concernent la gestion des feuilles :
- (void)beginSheet:(NSWindow *)sheet modalForWindow:(NSWindow *)docWindow modalDelegate:(id)modalDelegate didEndSelector:(SEL)didEndSelector contextInfo:(void *)contextInfo

Commence une feuille.


- (void)endSheet:(NSWindow *)sheet returnCode:(int)returnCode

Termine une feuille.

316

Cocoa

Outre la feuille et la fentre laquelle elle est attache, nous passons un dlgu modal, un slecteur et un pointeur pour crer une feuille. modalDelegate recevra le message indiqu par didEndSelector, et la feuille, son code de retour, ainsi que contextInfo seront passs en argument. La mthode invoque par didEndSelector doit donc avoir la signature suivante :
- (void)rex:(NSWindow *)sheet fido:(int)returnCode medor:(void *)contextInfo

Les noms de chiens signient que nous pouvons utiliser nimporte quels noms pour la mthode. La plupart des programmeurs choisissent des noms plus signicatifs, comme sheetDidEnd:returnCode:contextInfo:.

Ajouter une feuille


Nous allons ajouter une feuille qui permettra lutilisateur dajuster la vitesse de lapplication TypingTutor. Elle sera afche lorsque lutilisateur slectionnera lentre de menu Adjust speed. La feuille sera ferme en cliquant sur un bouton OK. La Figure 25.1 prsente lapplication termine.
Figure 25.1
Lapplication termine.

LAppController contrlera le curseur et la fentre. Nous ajouterons donc des outlets pour ces lments. Il recevra galement un message lorsque lutilisateur slectionnera lentre de menu Adjust speed ou cliquera sur le bouton OK. Nous ajouterons donc deux mthodes daction AppController. La Figure 25.2 prsente le graphe dobjets.

Chapitre 25

Feuilles

317

Figure 25.2
Graphe dobjets.

Ajouter les outlets et les actions Modiez AppController.h de la manire suivante :


#import <Cocoa/Cocoa.h> @class BigLetterView; @interface AppController: NSObject { // Outlets. IBOutlet BigLetterView *inLetterView; IBOutlet BigLetterView *outLetterView; IBOutlet NSWindow *speedSheet; // Donnes. NSArray *letters; int lastIndex; // Minuterie. NSTimer *timer; int count; int stepSize; } (IBAction)stopGo:(id)sender; (IBAction)showSpeedSheet:(id)sender; (IBAction)endSpeedSheet:(id)sender; (void)incrementCount; (void)resetCount; (void)showAnotherLetter;

@end

Enregistrez le chier.

318

Cocoa

Agencer linterface Ouvrez MainMenu.nib. Ajoutez une entre de menu dans le menu principal de lapplication en la faisant glisser depuis la bibliothque (sous Application > Menus), comme lillustre la Figure 25.3.
Figure 25.3
Ajouter une entre de menu.

Intitulez lentre Adjust Speed. En maintenant la touche Contrle enfonce, faites glisser lentre sur AppController et choisissez laction showSpeedSheet: (voir Figure 25.4).

Figure 25.4
Connecter lentre de menu.

Chapitre 25

Feuilles

319

Crez une nouvelle fentre en la faisant glisser depuis la bibliothque (sous Application > Windows). Dsactivez son redimensionnement et dcochez Visible at launch (voir Figure 25.5).
Figure 25.5
Inspecter la nouvelle fentre.

Placez un curseur sur la nouvelle fentre. Pour nommer lextrmit gauche du curseur Lent et lextrmit droite Rapide, dposez deux champs de texte non modiables sur la fentre. Ajoutez un bouton intitul OK. Attribuez au curseur la valeur minimale 1 et maximale 10 (voir Figure 25.6).
Figure 25.6
Inspecter le curseur.

320

Cocoa

Liez la valeur du curseur la variable stepSize dAppController (voir Figure 25.7).


Figure 25.7
Lier la valeur du curseur stepSize.

Lorsque lutilisateur clique sur le bouton OK, AppController doit recevoir un message qui terminera la feuille. En maintenant enfonce la touche Contrle, faites glisser le bouton sur AppController et choisissez endSpeedSheet: comme action (voir Figure 25.8).

Figure 25.8
Fixer la cible du bouton.

Pour que la fentre safche comme une feuille, AppController doit avoir un pointeur sur cette fentre. Faites un Contrle-clic sur AppController pour obtenir le panneau des connexions. Connectez loutlet speedSheet la fentre (voir Figure 25.9). Enregistrez et fermez le chier nib.

Chapitre 25

Feuilles

321

Figure 25.9
Connecter loutlet speedSheet.

Ajouter le code Dans AppController.m, nous avons dni la constante COUNT_STEP qui prcise le pas dincrmentation de la minuterie ; lutilisateur va de 0 100 par pas de 5. Dans cette section, nous allons lier le pas au curseur. Plus le curseur se rapprochera de lextrmit Rapide, plus le pas sera grand. Dans AppController.m, nous supprimons la ligne #define COUNT_STEP (5). Dans la mthode init, nous initialisons stepSize 5 :
- (id)init { [super init]; // Crer un tableau de lettres. letters = [[NSArray alloc] initWithObjects:@"a", @"s", @"d",@"f", @"j", @"k", @"l", @";",nil]; // Initialiser le gnrateur de nombres alatoires. srandom(time(NULL)); stepSize = 5; return self; }

Dans incrementCount, nous remplaons COUNT_STEP par stepSize :


- (void)incrementCount { [self willChangeValueForKey:@"count"]; count = count + stepSize;

322

Cocoa

if (count > MAX_COUNT) { count = MAX_COUNT; } [self didChangeValueForKey:@"count"]; }

Lorsque lutilisateur slectionne lentre de menu Adjust Speed, la feuille doit tre afche. Pour cela, ajoutez la mthode suivante AppController.m :
- (IBAction)showSpeedSheet:(id)sender { [NSApp beginSheet:speedSheet modalForWindow:[inLetterView window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; }

Nous attachons la feuille la fentre dans laquelle se trouve inLetterView. Par ailleurs, lorsque la feuille est ferme, aucune mthode nest rappele. La feuille doit tre ferme lorsque lutilisateur clique sur le bouton OK. Ajoutez la mthode suivante dans AppController.m :
- (IBAction)endSpeedSheet:(id)sender { // Revenir une gestion normale des vnements. [NSApp endSheet:speedSheet]; // Masquer la feuille. [speedSheet orderOut:sender]; }

Compilez et excutez lapplication. Ouvrez la feuille, ajustez la vitesse et fermez la feuille.

Pour les plus curieux : contextInfo


Le paramtre contextInfo est un pointeur sur des donnes. Nous pouvons passer ce paramtre lors de louverture de la feuille. Le dlgu recevra le pointeur lors de la fermeture de la feuille. Dans lexemple suivant, nous ouvrons une feuille et passons un numro de tlphone comme information de contexte :
[NSApp beginSheet:uneFenetre modalForWindow:uneAutreFenetre modalDelegate:self didEndSelector:@selector(didEnd:returnCode:telephone:) contextInfo:@"01-44-23-18-39"];

Chapitre 25

Feuilles

323

Dans la mthode didEnd:returnCode:telephone:, le numro de tlphone se trouve dans le troisime argument :


- (void)didEnd:(NSWindow *)feuille returnCode:(int)codeRetour telephone:(NSString *)numTel { NSLog(@"sheetDidEnd: Numro de tlphone = %@", numTel); }

Linformation de contexte et le dictionnaire des informations utilisateur de NSNotification jouent un rle quivalent.

Pour les plus curieux : fentres modales


Lorsquune feuille est active, lutilisateur ne peut plus envoyer dvnements la fentre laquelle elle est attache. Lorsquun panneau dalerte est afch, il est modal. Autrement dit, lutilisateur ne peut pas envoyer dvnements nimporte quelle autre fentre. Pour quune fentre soit modale, nous utilisons la mthode suivante de NSApp :
- (int)runModalForWindow:(NSWindow *)aWindow

Cette mthode ne laisse passer que les vnements destins aWindow. Lorsque aWindow ne doit plus tre modale, il suft denvoyer ce message lobjet NSApplication :
- (void)stopModalWithCode:(int)returnCode

La mthode runModalForWindow: se termine alors et retourne la valeur de returnCode.

26
NSFormatter
Au sommaire de ce chapitre
Formateur de base Le dlgu de NSControl Vrier des chanes partielles Formateurs qui retournent des chanes avec attributs

Un formateur prend une chane de caractres et gnre un autre objet, en gnral pour que lutilisateur puisse saisir autre chose quune simple chane. Par exemple, lorsquil reoit la chane "17/3/1975", NSDateFormatter la convertit en un objet NSDate qui reprsente le 17e jour du mois de mars de lanne 1975 (voir Figure 26.1).
Figure 26.1
NSDateFormatter.

326

Cocoa

Par ailleurs, un formateur peut prendre un objet et gnrer une chane prsente lutilisateur. Par exemple, imaginons un champ de texte qui contient un NSDateFormatter. Lorsque le champ de texte reoit le message setObjectValue: avec un objet NSCalendarDate, le formateur de date gnre une chane de caractres qui reprsente cette date. Lutilisateur voit alors cette chane. Tous les formateurs sont des sous-classes de NSFormatter. Cocoa en fournit deux : NSDateFormatter et NSNumberFormatter. Nous avons utilis NSNumberFormatter au Chapitre 8 pour mettre laugmentation espre sous forme de pourcentage. Le formateur de base implmente deux mthodes :
- (BOOL)getObjectValue:(id *)anObject forString:(NSString *)aString errorDescription:(NSString **)errorPtr

Ce message est envoy au formateur par le contrle, par exemple un champ de texte, lorsquil doit convertir aString, ce que lutilisateur a saisi, en un objet. Le formateur peut retourner YES et affecter anObject pour quil pointe sur le nouvel objet. Sil retourne NO, cela signie que la chane ne peut pas tre convertie et le problme est signal par lintermdiaire d errorPtr. Cet argument est un pointeur sur un pointeur. Autrement dit, il sagit dun emplacement o nous pouvons placer un pointeur sur la chane de caractres. De mme, anObject est un pointeur sur un pointeur.
- (NSString *)stringForObjectValue:(id)anObject

Ce message est envoy au formateur par le contrle lorsquil doit convertir anObject en une chane de caractres. Le contrle prsente la chane retourne lutilisateur (voir Figure 26.2).
Figure 26.2
NSFormatter.
@"hier"

Les formateurs convertissent les chanes en dautres types dobjets

et d'autres types d'objets en chanes.


@"%d %B %Y" @"17 aot 1967"

Chapitre 26

NSFormatter

327

Trs souvent, lobjet cr partir de la chane est galement une chane. Par exemple, nous pourrions crire une classe NumeroTelephoneFormatter qui insre les tirets et les parenthses autour du code international dans un numro de tlphone.

Formateur de base
Dans ce chapitre, nous crirons notre propre classe de mise en forme. Nous allons crer un formateur qui permet lutilisateur de saisir le nom dune couleur et qui la transforme en objet NSColor correspondant. Nous utiliserons ensuite cet objet de couleur pour modier la couleur darrire-plan de BigLetterView. La Figure 26.3 illustre lapplication termine.
Figure 26.3
Lapplication termine.

Crer ColorFormatter.h Dans Xcode, crez une nouvelle classe Objective-C nomme ColorFormatter. Dans ColorFormatter.h, changez la superclasse en NSFormatter et ajoutez une variable dinstance :
#import <Cocoa/Cocoa.h> @interface ColorFormatter: NSFormatter { NSColorList *colorList; } @end

Enregistrez le chier.

328

Cocoa

Modier le chier nib Ouvrez MainMenu.nib. Dposez un tmoin de couleur et un champ de texte sur la fentre (voir Figure 26.4).
Figure 26.4
Ajouter un tmoin de couleur et un champ de texte.

Liez la valeur du tmoin de couleur la variable inLetterView.bgColor dAppController (voir Figure 26.5).

Figure 26.5
Lier la valeur du tmoin de couleur la variable bgColor dinLetterView.

Chapitre 26

NSFormatter

329

Liez la valeur du champ de texte au mme chemin de cl (voir Figure 26.6).

Figure 26.6
Lier la valeur du champ de texte la variable bgColor dinLetterView.

Dposer un NSObject dans la fentre du chier nib et xez sa classe ColorFormatter (voir Figure 26.7).
Figure 26.7
Crer une instance de ColorFormatter.

Faites un Contrle-clic sur le champ de texte pour afcher le panneau de ses connexions. Connectez loutlet formatter lobjet ColorFormatter (voir Figure 26.8).

330

Cocoa

Figure 26.8
Connecter loutlet formatter du champ de texte.

NSColorList Pour cet exercice, nous utiliserons un NSColorList. Il sagit dun dictionnaire dobjets de couleur qui associe un nom une instance de NSColor. Plusieurs listes de couleur sont fournies en standard avec Mac OS X. En particulier, la liste de couleur nomme "Apple" comprend un grand nombre de couleurs standard, comme violet et jaune.
NSColorList nest pas une classe particulirement utile, mais elle rend cet exercice trs lgant. Nous nallons pas passer plus de temps la dcrire.

Rechercher des sous-chanes dans des chanes Si nous avons une chane, comme "dakakookookakoo" et que nous y recherchions une chane plus courte, comme "ka", le rsultat est un NSRange. La variable location indique lemplacement dans la chane de la premire lettre de la sous-chane qui correspond. La variable length indique la longueur de la sous-chane. Bien sr, nous avons quelques options notre disposition. Par exemple, nous pouvons faire une recherche insensible la casse ou une recherche inverse (de la n vers le dbut). Voici comment effectuer une recherche inverse insensible la casse de la chane "KA" dans "abbakachakaza" :
NSRange unRange; NSString *longue = @"abbakachakazazzz";

Chapitre 26

NSFormatter

331

NSString *courte = @"KA"; unRange = [longue rangeOfString:courte options:(NSCaseInsensitiveSearch | NSBackwardsSearch)];

Aprs excution de ce code, aRange.location vaut 9 et aRange.length vaut 2. Si la sous-chane est introuvable, length sera gale 0. Implmenter les mthodes Modiez ColorFormatter.m de la manire suivante :
#import "ColorFormatter.h" @interface ColorFormatter () - (NSString *)firstColorKeyForPartialString:(NSString *)string; @end @implementation ColorFormatter - (id)init { [super init]; colorList = [[NSColorList colorListNamed:@"Apple"] retain]; return self; } - (void)dealloc { [colorList release]; [super dealloc]; } - (NSString *)firstColorKeyForPartialString:(NSString *)string { // La cl est-elle vide? if ([string length] == 0) { return nil; } // Parcourir la liste de couleurs. for (NSString *key in [colorList allKeys]) { NSRange whereFound = [key rangeOfString:string options:NSCaseInsensitiveSearch]; // La chane correspond-elle au dbut du nom de la couleur? if ((whereFound.location == 0) && (whereFound.length > 0)) { return key; } } // Aucune correspondance trouve, retourner nil. return nil; }

332

Cocoa

- (NSString *)stringForObjectValue:(id)obj { // Est-ce une couleur? if (![obj isKindOfClass:[NSColor class]]) { return nil; } // Convertir en couleur RGB. NSColor *color; color = [obj colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; // Obtenir les composantes sous forme de nombres en virgule // flottante entre 0 et 1. CGFloat red, green, blue; [color getRed:&red green:&green blue:&blue alpha:NULL]; // Affecter une valeur leve la distance. float minDistance = 3.0; NSString *closestKey = nil; // Rechercher la couleur la plus proche. for (NSString *key in [colorList allKeys]) { NSColor *c = [colorList colorWithKey:key]; CGFloat r, g, b; [c getRed:&r green:&g blue:&b alpha:NULL]; // quelle distance se trouvent color et c? float dist; dist = pow(red - r, 2) + pow(green -g, 2) + pow(blue - b, 2); // Est-ce la distance la plus courte? if (dist < minDistance) { minDistance = dist; closestKey = key; } } // Retourner le nom de la couleur la plus proche. return closestKey; } - (BOOL)getObjectValue:(id *)obj forString:(NSString errorDescription:(NSString { // Rechercher la couleur qui NSString *matchingKey = [self

*)string **)errorString correspond string. firstColorKeyForPartialString:string];

Chapitre 26

NSFormatter

333

if (matchingKey) { *obj = [colorList colorWithKey:matchingKey]; return YES; } else { // Parfois, errorString vaut NULL. if (errorString!= NULL) { *errorString = [NSString stringWithFormat: @"%@ nest pas une couleur", string]; } return NO; } } @end

Compilez et excutez lapplication. Vous pouvez saisir des noms de couleurs et voir larrire-plan de BigLetterView changer en consquence. Par ailleurs, si vous utilisez le tmoin de couleur, vous pouvez voir le nom de la couleur changer dans le champ de texte. Essayez une chane qui ne correspond aucune couleur.

Le dlgu de NSControl
Le mcanisme des liaisons cre une feuille dalerte lorsque le formatage choue. Le dlgu du champ de texte peut galement tre inform de cet chec. Si le formateur dcide que la chane est invalide, le dlgu reoit le message derreur suivant :
- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)string errorDescription:(NSString *)error

Le dlgu peut contredire lavis du formateur. Sil retourne YES, le contrle afche la chane telle quelle. Sil retourne NO, le dlgu est daccord avec le formateur : la chane est bien invalide. Implmentez la mthode suivante dans AppController.m :
- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)string errorDescription:(NSString *)error { NSLog(@"AppController signale que le formatage de %@ a chou: %@", string, error); return NO; }

Ouvrez le chier nib et faites dAppController le dlgu du champ de texte (voir Figure 26.9). Compilez et excutez lapplication. Lorsque la validation choue, la console afche un message qui contient la chane et la raison de lchec.

334

Cocoa

Figure 26.9
Connecter loutlet delegate du champ de texte.

Vrier des chanes partielles


Nous pourrions avoir besoin dun formateur qui empche lutilisateur de saisir les lettres qui ne font pas partie dun nom de couleur. Pour que le formateur vrie la chane chaque appui sur une touche, nous implmentons la mthode suivante :
- (BOOL)isPartialStringValid:(NSString *)partial newEditingString:(NSString **)newString errorDescription:(NSString **)errorString

partial correspond la chane, y compris la dernire lettre saisie. Lorsque cette mthode retourne NO, cela signie que la chane partielle nest pas acceptable. De plus, le formateur peut fournir une nouvelle chane (newString) et une chane derreur (errorString). La chane newString sera afche dans le contrle. errorString doit donner lutilisateur une ide du problme. Si le formateur retourne YES, les arguments newString et errorString sont ignors.

Ajoutez la mthode suivante dans ColorFormatter.m :


- (BOOL)isPartialStringValid:(NSString *)partial newEditingString:(NSString **)newString errorDescription:(NSString **)error { // Les chanes vides sont valides. if ([partial length] == 0){ return YES; }

Chapitre 26

NSFormatter

335

NSString *match = [self firstColorKeyForPartialString:partial]; if (match) { return YES; } else { if (error) { *error = @"Aucune couleur ne correspond"; } return NO; } }

Compilez et excutez lapplication. Vous ne pourrez pas saisir autre chose que des noms de couleurs. Cette application a un petit dfaut. Lutilisateur ne connat pas la couleur choisie tant quil na pas quitt le champ de texte. Il serait prfrable que le formateur implmente une compltion automatique. Pour cela, nous devons contrler la plage de la slection. Placez en commentaire la mthode isPartialStringValid:newEditing-String :errorDescription: et remplacez-la par la suivante :
- (BOOL)isPartialStringValid:(NSString **)partial proposedSelectedRange:(NSRange *)selPtr originalString:(NSString *)origString originalSelectedRange:(NSRange)origSel errorDescription:(NSString **)error { // Les chanes vides sont valides. if ([*partial length] == 0) { return YES; } NSString *match = [self firstColorKeyForPartialString:*partial]; // Aucune couleur ne correspond? if (!match) { return NO; } // Si le dbut de la slection na pas boug, il sagit // dune suppression. if (origSel.location == selPtr->location) { return YES; } // Si la chane partielle est plus courte que la correspondance, // fournir la correspondance et fixer la slection. if ([match length]!= [*partial length]) { selPtr->location = [*partial length]; selPtr->length = [match length] - selPtr->location; *partial = match; return NO; } return YES; }

336

Cocoa

Compilez et excutez lapplication. Le formateur complte automatiquement les noms de couleurs pendant leur saisie.

Formateurs qui retournent des chanes avec attributs


Parfois, il peut tre intressant que le formateur dnisse non seulement la chane afcher, mais galement ses attributs. Par exemple, un formateur de nombres pourrait afcher les valeurs ngatives en rouge. Pour implmenter ce comportement, nous utiliserons NSAttributedString. Notre formateur peut implmenter la mthode suivante :
- (NSAttributedString *)attributedStringForObjectValue:(id)anObj withDefaultAttributes:(NSDictionary *)aDict

Si cette mthode existe, elle est invoque la place de stringForObjectValue:. Le dictionnaire pass contient les attributs par dfaut pour la vue dans laquelle les donnes seront afches. Il est prfrable de fusionner le dictionnaire avec les attributs que nous ajoutons. Par exemple, nous utilisons la police du champ de texte qui afche les donnes, mais choisissons le rouge comme couleur de premier plan pour indiquer des pertes. Ajoutez la mthode suivante an dafcher le nom de la couleur dans cette couleur :
- (NSAttributedString *)attributedStringForObjectValue:(id)anObj withDefaultAttributes:(NSDictionary *)attributes { NSMutableDictionary *md = [attributes mutableCopy]; NSString *match = [self stringForObjectValue:anObj]; if (match) { [md setObject:anObj forKey:NSForegroundColorAttributeName]; } else { match = @""; } NSAttributedString *atString; atString = [[NSAttributedString alloc] initWithString:match attributes:md]; [md release]; [atString autorelease]; return atString; }

Compilez et excutez lapplication. Notez que le champ de texte ne changera pas de couleur tant quil naura pas quitt son rle de premier rpondeur.

27
Impression
Au sommaire de ce chapitre

Grer la pagination Pour les plus curieux : suis-je en train de dessiner lcran ?

Le code de gestion de limpression est toujours assez complexe crire. De nombreux facteurs entrent en jeu : mise en page, marges et orientation de la page (portrait ou paysage). Ce chapitre a pour objectif de vous mettre sur la bonne voie, vers des impressions parfaites. Le dveloppement des mthodes dimpression est considrablement plus facile sous Mac OS X que sous la plupart des systmes dexploitation. En effet, les vues savent dj gnrer du PDF et Mac OS X sait imprimer ce format de chier. Si nous avons une application base sur les documents et une vue qui sait comment se dessiner, il nous suft dimplmenter printOperationWithSettings:error:. Dans cette mthode, nous crons un objet NSPrintOperation en utilisant une vue et le retournons. Dans la sousclasse de NSDocument, le code ressemble au suivant :
- (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)ps error:(NSError **)e; { NSPrintInfo *printInfo = [self printInfo]; NSPrintOperation *printOp = [NSPrintOperation printOperationWithView:aView printInfo:printInfo]; return printOp; }

338

Cocoa

Grer la pagination
Comment grer les pages multiples ? En effet, puisquune vue ne contient quune seule page, comment peut-elle imprimer des documents de plusieurs pages ? Hors cran, nous allons crer une vue gigantesque capable dafcher simultanment toutes les pages du document. Le systme dimpression demandera la vue le nombre de pages quelle afche. Ensuite, le systme demandera la vue lemplacement de chaque page (voir Figure 27.1).
Figure 27.1
Chaque page correspond un rectangle sur la vue.

La vue doit rednir les deux mthodes suivantes :


- (BOOL)knowsPageRange:(NSRange *)rptr

Retourne le nombre de pages.


- (NSRect)rectForPage:(int)pageNum

Retourne lemplacement dune page. titre dexemple, nous allons ajouter limpression lapplication RaiseMan. Nous imprimerons le nom et laugmentation espre pour autant de personnes que nous pourrons en placer sur la feuille de papier slectionne par lutilisateur dans le panneau Imprimer (voir Figure 27.2). Pour cela, nous allons crer une vue qui se chargera de limpression. Au lieu de crer une vue sufsamment grande pour afcher simultanment toutes les personnes, nous noterons simplement la page en cours dimpression par le systme et y dessinerons les noms dans drawRect:.

Chapitre 27

Impression

339

Figure 27.2
Lapplication termine.

Le code de MyDocument.m est assez simple :


- (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)ps error:(NSError **)e { PeopleView *view = [[PeopleView alloc] initWithPeople:employees]; NSPrintInfo *printInfo = [self printInfo]; NSPrintOperation *printOp = [NSPrintOperation printOperationWithView:view printInfo:printInfo]; [view release]; return printOp; }

Importez galement PeopleView.h au dbut de MyDocument.m. Dans le chier MainMenu.nib, vous remarquerez que lentre de menu Print a encore nil pour cible et que son action est printDocument:. printDocument: invoquera printOperationWithSettings:error: (voir Figure 27.3). Crez une sous-classe de NSView nomme PeopleView. Voici le contenu de PeopleView.h :
#import <Cocoa/Cocoa.h> @interface PeopleView: NSView { NSArray *people; NSMutableDictionary *attributes; float lineHeight; NSRect pageRect; int linesPerPage; int currentPage; } - (id)initWithPeople:(NSArray *)array; @end

340

Cocoa

Figure 27.3
Connecter lentre de menu.

Dans PeopleView.m, nous implmentons la mthode initWithPeople:. Cet initialiseur appelle la mthode initWithFrame: de NSView :
#import "PeopleView.h" #import "Person.h" @implementation PeopleView - (id)initWithPeople:(NSArray *)persons { // Appeler linitialiseur dsign de la super-classe // avec un cadre factice. [super initWithFrame:NSMakeRect(0, 0, 700, 700)]; people = [persons copy]; // Les attributs du texte afficher. attributes = [[NSMutableDictionary alloc] init]; NSFont *font = [NSFont fontWithName:@"Monaco" size:12.0]; lineHeight = [font capHeight] * 1.7; [attributes setObject:font forKey:NSFontAttributeName]; return self; }

Chapitre 27

Impression

341

- (void)dealloc { [people release]; [attributes release]; [super dealloc]; } #pragma mark Pagination - (BOOL)knowsPageRange:(NSRange *)range { NSPrintOperation *po = [NSPrintOperation currentOperation]; NSPrintInfo *printInfo = [po printInfo]; // O puis-je dessiner? pageRect = [printInfo imageablePageBounds]; NSRect newFrame; newFrame.origin = NSZeroPoint; newFrame.size = [printInfo paperSize]; [self setFrame:newFrame]; // Combien de lignes par page? linesPerPage = pageRect.size.height / lineHeight; // Une page par feuille. range->location = 1; // Nombre de pages ncessaires? range->length = [people count] / linesPerPage; if ([people count] % linesPerPage) { range->length = range->length + 1; } return YES; } - (NSRect)rectForPage:(int)i { // Noter la page en cours. currentPage = i - 1; // Retourner chaque fois le mme rectangle de page. return pageRect; } #pragma mark Drawing // Lorigine de la vue se trouve dans le coin suprieur gauche. - (BOOL)isFlipped { return YES; } - (void)drawRect:(NSRect)r { NSRect nameRect; NSRect raiseRect;

342

Cocoa

raiseRect.size.height = nameRect.size.height = lineHeight; nameRect.origin.x = pageRect.origin.x; nameRect.size.width = 200.0; raiseRect.origin.x = NSMaxX(nameRect); raiseRect.size.width = 100.0; int i; for (i=0; i<linesPerPage; i++) { int index = (currentPage * linesPerPage) + i; if (index >= [people count]) { break; } Person *p = [people objectAtIndex:index]; // Tracer lindice et le nom. nameRect.origin.y = pageRect.origin.y + (i * lineHeight); NSString *nameString = [NSString stringWithFormat:@"%2d %@", index, [p personName]]; [nameString drawInRect:nameRect withAttributes:attributes]; raiseRect.origin.y = nameRect.origin.y; NSString *raiseString=[NSString stringWithFormat:@"%4.1f%%", [p expectedRaise]]; [raiseString drawInRect:raiseRect withAttributes:attributes]; } } @end

Compilez et excutez lapplication. Vous remarquerez que la conguration plusieurs pages par feuille, par exemple 4 de gauche droite, est oprationnelle. Si vous changez le format du papier, le nombre de personnes imprimes sur la page volue.

Pour les plus curieux : suis-je en train de dessiner lcran ?


Dans une application, le trac lcran est souvent diffrent du trac sur limprimante. Par exemple, dans un programme de dessin, la vue peut afcher une grille lcran, mais pas limprimer sur le papier. Dans notre mthode drawRect:, nous pouvons demander au contexte graphique actuel si la destination du dessin est lcran :
if ([[NSGraphicsContext currentContext] isDrawingToScreen]) { ...tracer la grille... }

Exercice
Ajoutez les numros de pages lors de limpression.

28
Services web
Au sommaire de ce chapitre
AmaZone Agencer linterface crire le code

Les services web sont la mode. En ralit, il sagit simplement dune requte et dune rponse HTTP qui transportent des donnes XML. Par consquent, pour utiliser un service web partir de Cocoa, il suft dtre capable denvoyer des requtes HTTP, de recevoir des rponses HTTP et de gnrer et danalyser le contenu XML. Ce processus est illustr la Figure 28.1.
Figure 28.1
Un service web en action.

Les requtes et les rponses HTTP sont laffaire de NSURL, NSURLRequest et NSURLConnection (voir Figure 28.2).

Figure 28.2
Classes de manipulation des requtes HTTP.

344

Cocoa

La gnration et lanalyse dun contenu XML se font gnralement avec NSXMLDocument et NSXMLNode. Supposons que nous ayons un NSData qui contient le balisage XML suivant :
<?xml version="1.0" encoding="UTF-8"?> <personne> <prenom>Annie</prenom> <nom>Versaire</nom> </personne>

NSXMLDocument le convertit en une arborescence, comme lillustre la Figure 28.3.

Figure 28.3
Document XML analys.

AmaZone
Dans cet exercice, nous allons dvelopper une application qui utilise le service web dAmazon pour rechercher les livres partir de mots cls. Le rsultat de la recherche sera afch dans une vue tableau. Un double-clic sur une ligne ouvrira la page correspondant au livre dans le navigateur web. La Figure 28.4 prsente lapplication termine.
Figure 28.4
Lapplication termine.

Chapitre 28

Services web

345

Crez un nouveau projet de type Cocoa Application. Nommez-le AmaZone. Crez la classe Objective-C AppController. Dans AppController.h, dclarez trois outlets et une action :
#import <Cocoa/Cocoa.h> @interface AppController: NSObject { IBOutlet NSProgressIndicator *progress; IBOutlet NSTextField *searchField; IBOutlet NSTableView *tableView; } - (IBAction)fetchBooks:(id)sender; @end

Agencer linterface
Ouvrez MainMenu.nib et dposez un NSObject dans la fentre du chier nib. Dans linspecteur Identity, xez la classe de cet objet AppController (voir Figure 28.5).
Figure 28.5
Crer lAppController.

Dposez un champ de texte, un bouton, un indicateur de progression et une vue tableau (avec trois colonnes) dans la fentre (voir Figure 28.6). Congurez la cible et laction du bouton pour quil invoque la mthode fetchBooks: dAppController.

346

Cocoa

Figure 28.6
Linterface de base.

Faites un Contrle-clic sur AppController pour connecter les trois outlets tableView, searchField et progress. Faites un Contrle-clic sur la vue tableau pour afcher le panneau de ses connexions. Faites dAppController la source de donnes de la vue tableau. Si vous ne voyez pas loutlet dataSource, vriez que vous avez bien slectionn la vue tableau, non la vue dlement. Lorsquil appuie sur la touche Entre, lutilisateur sattend ce que la recherche soit dclenche. En maintenant la touche Contrle enfonce, faites glisser le champ de texte sur le bouton. Choisissez laction performClick:. Dans linspecteur Attributes du champ de texte, congurez celui-ci pour quil envoie son action uniquement suite lappui sur la touche Entre (voir Figure 28.7).

Figure 28.7
Attributs du champ de texte.

Chapitre 28

Services web

347

Les donnes XML peuvent tre vues comme un arbre constitu de nuds. Aprs avoir rcupr les donnes auprs dAmazon, nous disposerons dun tableau dobjets NSXMLNode, un pour chaque ligne de la vue tableau. Pour obtenir les donnes partir dun nud XML, une solution consiste utiliser XPath. XPath pourrait tre compar aux chemins de cls, mais en utilisant "/" comme sparateur la place de ".". Pour simplier le code, nous allons xer lidentiant de chaque colonne du tableau au XPath des donnes que nous voulons y afcher. En consultant la documentation des services web dAmazon, nous savons que les identiants seront ASIN, ItemAttributes/Title et ItemAttributes/Author. Fixez lidentiant de chaque colonne dans linspecteur Attributes. Protez-en pour rendre les colonnes non modiables (voir Figure 28.8).

Figure 28.8
Fixer les identiants des colonnes.

crire le code
Dans AppController.h, nous ajoutons des variables dinstance qui contiendront lintgralit du document XML et un tableau des nuds qui reprsentent les livres :
NSXMLDocument *doc; NSArray *itemNodes;

Dans AppController.m, nous aurons besoin dun identiant AWS. Si cet identiant nest plus oprationnel, cela vient du fait quune personne en a fait un usage abusif, empchant quiconque de lutiliser. Dans ce cas, vous devrez contacter Amazon Developer Support pour en obtenir un nouveau.
#define AWS_ID @"1CKE6MZ6S27EFQ458402"

348

Cocoa

Cette ligne est une macro du prprocesseur. Vriez quelle ne contient aucun caractre "=" ou ";". Nous allons prsent combiner une URL et une requte dURL. Lorsque nous excutons la requte, nous recevons en retour un NSData. Les donnes sont au format XML et nous les convertissons en NSXMLDocument. Enn, nous utilisons XPath pour rcuprer le tableau des nuds. Ajoutez la mthode suivante dans AppController.m :
- (IBAction)fetchBooks:(id)sender { // Indiquer lutilisateur quune opration est en cours. [progress startAnimation:nil]; // Construire la requte. // Voir http://www.amazon.com/gp/aws/landing.html // Prparer la chane son insertion dans une URL. NSString *input = [searchField stringValue]; NSString *searchString = [input stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; NSLog(@"searchString = %@", searchString); // Crer lURL (une longue chane dcoupe en plusieurs lignes // est valide). NSString *urlString = [NSString stringWithFormat: @"http://ecs.amazonaws.com/onca/xml?" @"Service=AWSECommerceService&" @"AWSAccessKeyId=%@&" @"Operation=ItemSearch&" @"SearchIndex=Books&" @"Keywords=%@&" @"Version=2007-07-16", AWS_ID, searchString]; NSURL *url = [NSURL URLWithString:urlString]; NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30]; // Recuprer la rponse XML. NSData *urlData; NSURLResponse *response; NSError *error; urlData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error]; if (!urlData) { NSAlert *alert = [NSAlert alertWithError:error]; [alert runModal]; return; } // Analyser la rponse XML. [doc release]; doc = [[NSXMLDocument alloc] initWithData:urlData

Chapitre 28

Services web

349

options:0 error:&error]; NSLog(@"doc = %@", doc); if (!doc) { NSAlert *alert = [NSAlert alertWithError:error]; [alert runModal]; return; } [itemNodes release]; itemNodes = [[doc nodesForXPath:@"ItemSearchResponse/Items/Item" error:&error] retain]; if (!itemNodes) { NSAlert *alert = [NSAlert alertWithError:error]; [alert runModal]; return; } // Actualiser linterface. [tableView reloadData]; [progress stopAnimation:nil]; }

Il serait prfrable de tester tout ce que nous venons de programmer. Ajoutez une mthode factice servant de source de donnes la vue tableau :
-(int)numberOfRowsInTableView:(NSTableView*)tv { return 0; }

Compilez et excutez lapplication. Vous ne verrez apparatre aucun titre dans la vue tableau, mais la console doit afcher les donnes XML reues. Si les donnes XML contiennent des intituls de livres, poursuivez. Si vous obtenez un message derreur, utilisez-le pour comprendre ce qui ne fonctionne pas. Nous disposons prsent dun tableau de nuds. Nous crivons une mthode qui nous permettra den extraire les donnes dont nous aurons besoin (dans AppController.m) :
- (NSString *)stringForPath:(NSString *)xp ofNode:(NSXMLNode *)n { NSError *error; NSArray *nodes = [n nodesForXPath:xp error:&error]; if (!nodes) { NSAlert *alert = [NSAlert alertWithError:error]; [alert runModal]; return nil; } if ([nodes count] == 0) { return nil; } else { return [[nodes objectAtIndex:0] stringValue]; } }

350

Cocoa

AppController reprsente la source de donnes pour la vue tableau. Ajoutez les mthodes dune source de donnes dans AppController.m :
#pragma mark TableView data source methods

- (int)numberOfRowsInTableView:(NSTableView *)tv { return [itemNodes count]; } - (id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row { NSXMLNode *node = [itemNodes objectAtIndex:row]; NSString *xPath = [tableColumn identifier]; return [self stringForPath:xPath ofNode:node]; }

Compilez et excutez lapplication. Vous devriez tre en mesure de rcuprer des titres depuis Amazon. La dernire tape consiste grer le double-clic de lutilisateur sur un titre an douvrir la page correspondante dans le navigateur. Dans awakeFromNib, xez les valeurs de doubleAction et de target de la vue tableau :
- (void)awakeFromNib { [tableView setDoubleAction:@selector(openItem:)]; [tableView setTarget:self]; }

Pour nir, nous implmentons openItem:. Pour cela, nous utilisons la classe NSWorkspace qui reprsente le Finder.
- (void)openItem:(id)sender { int row = [tableView clickedRow]; if (row == -1) { return; } NSXMLNode *clickedItem = [itemNodes objectAtIndex:row]; NSString *urlString = [self stringForPath:@"DetailPageURL" ofNode:clickedItem]; NSURL *url = [NSURL URLWithString:urlString]; [[NSWorkspace sharedWorkspace] openURL:url]; }

Compilez et excutez lapplication. En double-cliquant sur un titre, le navigateur par dfaut doit ouvrir la page correspondante sur le site dAmazon.

Chapitre 28

Services web

351

Dans cet exemple, le code bloque en attendant le retour des donnes. Nous pouvons galement effectuer des requtes dURL asynchrones. Le dlgu de lobjet NSURLConnection sera inform de larrive des donnes demandes.

Exercice : ajouter une vue web


Pour le moment, nous utilisons NSWorkspace pour ouvrir la page web dans une autre application. Lutilisateur souhaiterait peut-tre que la page web apparaisse dans une feuille dans lapplication existante (voir Figure 28.9).
Figure 28.9
Utiliser une vue web pour afcher les dtails.

Lexercice consiste donc ajouter une nouvelle fentre avec un WebView dans lapplication. Cette fentre doit safcher lcran comme une feuille. Vous aurez besoin dajouter le framework WebKit dans votre projet. Si vous disposez dune chane reprsentant une URL, vous pouvez demander au WebView de charger cette URL en lui envoyant le message suivant :
- (void)setMainFrameURL:(NSString *)URLString

Cest tout ce dont vous devriez avoir besoin. Si vous souhaitez ajouter un indicateur de progression, vous devrez faire de votre contrleur le "dlgu de chargement" de la vue web :
[webView setFrameLoadDelegate:self];

Le contrleur implmente ensuite les mthodes suivantes :


- (void)webView:(WebView *)wv didStartProvisionalLoadForFrame:(WebFrame *)wf

352

Cocoa

- (void)webView:(WebView *)wv didFinishLoadForFrame:(WebFrame *)wf - (void)webView:(WebView *)wv didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)wf

29
change de vues
Au sommaire de ce chapitre Conception Redimensionner la fentre Au lieu douvrir une nouvelle fentre, nous voudrons parfois retirer une vue et la remplacer par une autre. Pour y parvenir, une solution simple consiste changer la vue contenu dune bote. En plaant chaque vue dans son propre chier nib, nous obtenons une conception plus modulaire. Dans Mac OS 10.5, Apple a ajout la classe Cocoa NSViewController. Nous allons crer une sous-classe de NSViewController pour chaque vue qui pourra intgrer la bote. Au chapitre suivant, nous ferons voluer ce projet en une application Core Data relativement complexe. Nous voudrons donc que nos contrleurs de vues aient accs un NSManagedObjectContext. La Figure 29.1 illustre notre objectif.
Figure 29.1
Lapplication termine.

354

Cocoa

La liste droulante permettra lutilisateur de passer dune vue une autre. Dans ce chapitre, nous allons raliser le basculement entre les vues. Toutes les parties rellement utiles de lapplication seront crites au chapitre suivant.

Conception
Chaque vue, sous le contrle de contrleurs de vues, deviendra la vue contenu dune bote. Les entres de la liste droulante dclencheront le basculement entre les vues. La Figure 29.2 prsente le graphe des objets impliqus.
Figure 29.2
Graphe dobjets.
- changeViewController: popUp managedObjectContext box viewControllers NSBox NSMutableArray - setContentView: NSManaged ObjectContext MyDocument target target NSPopUpButton NSMenuItem tag = 0 title = "Employees" NSMenuItem tag = 1 title = "Departments"

managedObjectContext DepartmentViewController title = "Departments" view NSView

EmployeeViewController title = "Employees"

view

NSView

Crer les bases Dans Xcode, crez une nouvelle application de type Core Data Document-based Application nomme Departments. Ouvrez le chier MyDocument.nib et ajoutez une bote et une liste droulante (voir Figure 29.3).
Figure 29.3
La fentre de base.

Chapitre 29

change de vues

355

Double-cliquez sur la liste droulante pour ouvrir le menu et retirez toutes les entres existantes. Nous les crerons depuis le code. Dans MyDocument.h, ajoutez deux outlets, un tableau et une action :
#import <Cocoa/Cocoa.h> @interface MyDocument: NSPersistentDocument { IBOutlet NSBox *box; IBOutlet NSPopUpButton *popUp; NSMutableArray *viewControllers; } - (IBAction)changeViewController:(id)sender; @end

Enregistrez le chier. Revenez dans Interface Builder, faites un Contrle-clic sur Files Owner pour afcher le panneau de connexion et congurez les deux outlets. En maintenant la touche Contrle enfonce, faites glisser la liste droulante sur Files Owner an de dnir sa cible. Laction doit tre changeViewController:. Enregistrez le chier nib. Crer la classe ManagingViewController Dans Xcode, crez une nouvelle classe Objective-C nomme ManagingViewController. Nous hritons de NSViewController an que tous nos contrleurs de vues possdent un NSManagedObjectContext. Modiez le chier ManagingViewController.h :
#import <Cocoa/Cocoa.h> @interface ManagingViewController: NSViewController { NSManagedObjectContext *managedObjectContext; } @property (retain) NSManagedObjectContext *managedObjectContext; @end

Ajoutez les mthodes de gestion de cette variable dinstance dans ManagingViewController.m :


#import "ManagingViewController.h" @implementation ManagingViewController @synthesize managedObjectContext; - (void)dealloc { [managedObjectContext release]; [super dealloc]; } @end

356

Cocoa

Crer les contrleurs de vues et leur chier nib Nous allons prsent crer les deux vues spares qui seront changes dans la bote que nous avons ajoute la fentre. Chaque vue possde son propre contrleur, une sous-classe de ManagingViewController. Nous allons donc rpter deux fois les mmes tapes de base :
m

Crer une sous-classe de ManagingViewController pour jouer le rle de Files Owner. Crer un chier nib pour la vue.

Lune des vues permettra de consulter les services dune entreprise, lautre vue, ses employs. Nous allons commencer par la vue des services. Dans Xcode, crez une classe Objective-C nomme DepartmentViewController. Dans DepartmentViewController.h, faites-en une sous-classe de ManagingViewController :
#import "ManagingViewController.h" @interface DepartmentViewController: ManagingViewController

Dans Xcode, crez un chier nib vide nomm DepartmentView et ouvrez-le. partir de la bibliothque, faites glisser un NSView (intitul Custom View) dans la fentre du chier nib (voir Figure 29.4).
Figure 29.4
Crer une instance de NSView.

Chapitre 29

change de vues

357

Ajoutez quelques champs de texte et deux boutons sur la vue. Nous nutiliserons pas ces contrles, mais ils sont l pour afcher quelque chose dintressant lorsque la vue apparatra dans la bote. Fixez la classe de Files Owner DepartmentViewController. Faites un Contrle-clic sur Files Owner et affectez la vue intitule Custom View loutlet view (voir Figure 29.5).
Figure 29.5
Prsenter le contrleur de vue sa vue.

Loutlet view est dnie dans NSViewController. Enregistrez le chier nib. Dans la mthode init de DepartmentViewController.m, nous attribuons un chier nib et un titre au contrleur :
- (id)init { if (![super initWithNibName:@"DepartmentView" bundle:nil]) { return nil; } [self setTitle:@"Departments"]; return self; }

Reproduisez la mme opration pour EmployeeViewController :


m

Crez une sous-classe de ManagingViewController nomme EmployeeViewController. Crez un chier nib nomm EmployeeView.nib, avec une vue. Placez des contrles sur la vue. Fixez la classe de Files Owner EmployeeViewController. Affectez la vue loutlet view de Files Owner.

m m m m

358

Cocoa

Ajoutez une mthode init dans EmployeeViewController.m :


- (id)init { if (![super initWithNibName:@"EmployeeView" bundle:nil]) { return nil; } [self setTitle:@"Employees"]; return self; }

Ajouter lchange de vues dans MyDocument Nous devons prsent crer des instances des contrleurs dans MyDocument et les insrer dans le tableau viewControllers. Ajoutez le code suivant dans MyDocument.m :
#import "MyDocument.h" #import "DepartmentViewController.h" #import "EmployeeViewController.h" @implementation MyDocument - (id)init { [super init]; viewControllers = [[NSMutableArray alloc] init]; ManagingViewController *vc; vc = [[DepartmentViewController alloc] init]; [vc setManagedObjectContext:[self managedObjectContext]]; [viewControllers addObject:vc]; [vc release]; vc = [[EmployeeViewController alloc] init]; [vc setManagedObjectContext:[self managedObjectContext]]; [viewControllers addObject:vc]; [vc release]; return self; }

Nous librons le tableau viewControllers dans la mthode dealloc :


- (void)dealloc { [viewControllers release]; [super dealloc]; }

Voici la mthode qui place une vue dans la bote :


- (void)displayViewController:(ManagingViewController *)vc { // Tenter de terminer la modification. NSWindow *w = [box window]; BOOL ended = [w makeFirstResponder:w];

Chapitre 29

change de vues

359

if (!ended) { NSBeep(); return; } // Placer la vue dans la bote. NSView *v = [vc view]; [box setContentView:v]; }

Nous dclarons cette mthode dans MyDocument.h. Par ailleurs, le compilateur doit connatre lexistence de la classe ManagingViewController :
@class ManagingViewController; ... - (void)displayViewController:(ManagingViewController *)vc; ...

Une liste droulante peut tre vue comme un bouton avec un menu. Lorsque le chier nib est charg, nous devons ajouter au menu une entre pour chaque contrleur. Ajoutez le code suivant dans MyDocument.m :
- (void)windowControllerDidLoadNib:(NSWindowController *)wc { [super windowControllerDidLoadNib:wc]; NSMenu *menu = [popUp menu]; int i, itemCount; itemCount = [viewControllers count]; for (i = 0; i < itemCount; i++) { NSViewController *vc = [viewControllers objectAtIndex:i]; NSMenuItem *mi = [[NSMenuItem alloc] initWithTitle:[vc title] action:@selector(changeViewController:) keyEquivalent:@""]; [mi setTag:i]; [menu addItem:mi]; [mi release]; } // Afficher initialement le premier contrle. [self displayViewController:[viewControllers objectAtIndex:0]]; [popUp selectItemAtIndex:0]; }

La balise de lentre de menu est xe lindice du contrleur correspondant dans le tableau viewControllers. Nous utilisons cette balise dans la mthode daction dclenche par lentre de menu :
- (IBAction)changeViewController:(id)sender { int i = [sender tag]; ManagingViewController *vc = [viewControllers objectAtIndex:i]; [self displayViewController:vc]; }

Compilez et excutez lapplication. La liste droulante doit vous permettre de passer dune vue lautre.

360

Cocoa

Redimensionner la fentre
Que se passe-t-il si les deux vues sont de tailles radicalement diffrentes ? Ne serait-il pas plus agrable que la fentre adapte la taille de la bote celle de la vue ? Cest ce que nous allons faire prsent. Ouvrez les chiers nib des vues et donnez des tailles diffrentes aux vues. Dans MyDocument.nib, slectionnez la bote et, dans linspecteur Size, congurez-la pour quelle sadapte la taille de la fentre (voir Figure 29.6).
Figure 29.6
Inspecteur Size pour la bote.

Slectionnez la fentre. Dans linspecteur Attributes, interdisez lutilisateur de redimensionner la fentre (voir Figure 29.7). Dans MyDocument.m, compltez la mthode displayViewController: :
- (void)displayViewController:(ManagingViewController *)vc { // Tenter de terminer la modification. NSWindow *w = [box window]; BOOL ended = [w makeFirstResponder:w]; if (!ended) { NSBeep(); return; } NSView *v = [vc view]; // Calculer le nouveau cadre de la fentre. NSSize currentSize = [[box contentView] frame].size;

Chapitre 29

change de vues

361

NSSize newSize = [v frame].size; float deltaWidth = newSize.width - currentSize.width; float deltaHeight = newSize.height - currentSize.height; NSRect windowFrame = [w frame]; windowFrame.size.height += deltaHeight; windowFrame.origin.y -= deltaHeight; windowFrame.size.width += deltaWidth; // Effacer la bote pour le redimensionnement. [box setContentView:nil]; [w setFrame:windowFrame display:YES animate:YES]; [box setContentView:v]; }

Figure 29.7
Dsactiver le redimensionnement dune fentre.

Compilez et excutez lapplication. Lorsque les vues changent, la taille de la fentre doit sadapter la nouvelle vue.

30
Relations Core Data
Au sommaire de ce chapitre
Modier le modle Crer des classes NSManagedObject personnalises Agencer linterface vnements et nextResponder

Il est temps prsent de plonger un peu plus profond dans Core Data. Au Chapitre 11, nous avons manipul une seule entit (Car). La plupart des applications greront de multiples entits, connectes par plusieurs relations. Core Data prend en charge les relations " un" et les relations " plusieurs" non ordonnes. Cet exercice implique deux entits : Employee et Department. Un employ travaille dans un service. Un service possde plusieurs employs. Lun des employs du service est galement un directeur. Il existe donc trois relations, comme lillustre la Figure 30.1.
Figure 30.1
Le modle de donnes.

364

Cocoa

Modier le modle
Dans cette section, nous allons poursuivre le projet Departments commenc au chapitre prcdent. Dans Xcode, slectionnez MyDocument.xcdatamodel. Ajoutez les deux entits Employee et Department. Un Employee aura les attributs firstName et lastName, de type chane de caractres, ainsi quune relation " un", nomme department, avec Department. Ajoutez ces proprits (voir Figure 30.2). Notez que vous ne pouvez pas xer linverse tant que lentit Department nest pas cre.
Figure 30.2
Lentit Employee.

Un Department aura lattribut deptName, de type chane de caractres, ainsi quune relation " plusieurs", nomme employees, avec Employee. Enn, un Department aura une relation " plusieurs", nomme manager, avec Employee. Ajoutez ces proprits (voir Figure 30.3).
Figure 30.3
Lentit Department.

La relation department dEmployee et la relation employees de Department sont les inverses lune de lautre. Vriez que linverse est x pour ces relations, comme le montrent les Figures 30.2 et 30.3. La relation manager na pas dinverse. Lors de la compilation, vous risquez dobtenir un avertissement ce propos ; ignorez-le.

Chapitre 30

Relations Core Data

365

Crer des classes NSManagedObject personnalises


Puisque nous afcherons le nom complet dun employ, nous allons crer une classe qui conserve les informations concernant un employ. Elle sera une sous-classe de NSManagedObject. Un service ne peut tre dirig que par un employ qui travaille dans ce service. Lorsque lemploy directeur quitte le service, nous devons affecter nil la variable correspondante directeur. Cette opration sera assure par la sous-classe de NSManagedObject pour lentit Department. Le chier MyDocument.xcdatamodel tant ouvert dans lditeur, slectionnez lune des entits. Dans Xcode, choisissez File > New File. Slectionnez Managed Object Class. Crez des classes pour Employee et Department. Les mthodes de validation ne sont pas utiles pour ces classes (voir Figure 30.4).
Figure 30.4
Crer les classes personnalises.

Employee Dans Employee.h, dclarez la proprit en lecture seule fullName :


#import <CoreData/CoreData.h> @class Department; @interface Employee: NSManagedObject { } @property (retain) NSString *firstName; @property (retain) NSString *lastName; @property (retain) Department *department; @property (readonly) NSString *fullName; @end

366

Cocoa

Dans Employee.m, implmentez la mthode fullName :


- (NSString *)fullName { NSString *first = [self firstName]; NSString *last = [self lastName]; if (!first) return last; if (!last) return first; return [NSString stringWithFormat:@"%@ %@", first, last]; }

Nous allons lier la colonne dun tableau la cl fullName. Si les variables firstName ou lastName sont modies, les observateurs de fullName doivent tre informs de ces changements. Nous rednissons une mthode de classe pour prciser les cls qui sont lorigine des modications de fullName :
+ (NSSet *)keyPathsForValuesAffectingFullName { return [NSSet setWithObjects:@"firstName", @"lastName", nil]; }

Department Dans Department.h, nous indiquons que nous allons fournir des mthodes dajout et de suppression des employs :
#import <CoreData/CoreData.h> @class Employee; @interface Department: NSManagedObject { } @property (retain) NSString *deptName; @property (retain) Employee *manager; @property (retain) NSSet *employees; @end @interface Department (CoreDataGeneratedAccessors) - (void)addEmployeesObject*Employee *)value; - (void)removeEmployeesObject:Employee *)value; @end

Ces mthodes sont implmentes dans Department.m. Mme si seule la mthode de suppression nous intresse, elles viennent en couple. La suppression sera ignore, moins que nous ayons galement implment lajout.
#import "Department.h" #import "Employee.h" @implementation Department

Chapitre 30

Relations Core Data

367

@dynamic deptName; @dynamic manager; @dynamic employees; - (void)addEmployeesObject:(Employee *)value { NSLog(@"Service %@, ajout de lemploy %@", [self deptName], [value fullName]); NSSet *s = [NSSet setWithObject:value]; [self willChangeValueForKey:@"employees" withSetMutation:NSKeyValueUnionSetMutation usingObjects:s]; [[self primitiveValueForKey:@"employees"] addObject:value]; [self didChangeValueForKey:@"employees" withSetMutation:NSKeyValueUnionSetMutation usingObjects:s]; } - (void)removeEmployeesObject:(Employee *)value { NSLog(@"Service %@, suppression de lemploy %@", [self deptName], [value fullName]); Employee *manager = [self manager]; if (manager == value) { [self setManager:nil]; } NSSet *s = [NSSet setWithObject:value]; [self willChangeValueForKey:@"employees" withSetMutation:NSKeyValueMinusSetMutation usingObjects:s]; [[self primitiveValueForKey:@"employees"] removeObject:value]; [self didChangeValueForKey:@"employees" withSetMutation:NSKeyValueMinusSetMutation usingObjects:s]; } @end

Agencer linterface
Avant de modier les chiers nib, sachez que cet exercice implique un grand nombre de liaisons. Soyez patient. Noubliez pas que dans cet ouvrage nous ne crons jamais de liaisons avec une vue dlement, une vue tableau ou une cellule. En revanche, nous tablissons des liaisons avec des colonnes de tableaux. Faites bien attention au titre de la fentre de linspecteur an de vrier les cibles des liaisons. DepartmentView.nib Dans DepartmentView.nib, placez deux boutons, deux vues tableau et une liste droulante. Ajoutez une tiquette intitule Directeur au-dessus de la liste. Regroupez

368

Cocoa

ltiquette, la liste droulante et une vue tableau dans une bote. Crez trois contrleurs de tableaux nomms Depts, ManagerPopUp et EmployeeList. La Figure 30.5 rcapitule ces oprations.
Figure 30.5
Linterface de base.

Fixer la cible des deux boutons au contrleur de tableau Depts. Laction du bouton Nouveau doit tre add:. Celle du bouton Supprimer doit tre remove:. Le contrleur de tableau Depts doit tre en mode Entity et obtenir ses donnes depuis lentit Department. Cochez la case Prepares Content pour quil les prenne ds que le chier nib est charg (voir Figure 30.6).
Figure 30.6
Attributs du contrleur de tableau Depts.

Chapitre 30

Relations Core Data

369

partir du Tableau 30.1 et de linspecteur Bindings, dnissez lensemble des liaisons.


Tableau 30.1 : Liaisons pour DepartmentView.nib

Objet CT1 Depts

Liaison MOC2

Vers
Files Owner

Cl du contrleur Chemin de cl
managedObject Context selection selection arrangedObjects canRemove fullName employees employees deptName

CT ManagerPopUp Content set CT EmployeeList Content set Colonne Services Value Bouton Supprimer Enabled Colonne Employs Value Bouton pop-up Bouton pop-up Bouton pop-up Bote Content

CT Depts CT Depts CT Depts CT Depts

CT EmployeeList arrangedObjects CT ManagerPopUp arrangedObjects

Content values CT ManagerPopUp arrangedObjects Selected object Title CT Depts CT Depts


selection selection

fullName manager deptName

1. CT = Contrleur de tableau 2. MOC = Managed Object Context

Pour la dernire liaison, utilisez "Pas de slection" pour No Selection Placeholder, et "Service sans nom" pour Null Placeholder. Vous pouvez compiler et excuter lapplication. Vous devriez pouvoir ajouter et supprimer des services. EmployeeView.nib Ouvrez EmployeeView.nib et supprimez tous les contrles existants. Ajoutez une vue tableau avec trois colonnes. Placez une cellule de liste droulante dans la troisime colonne. Ajoutez des boutons Nouvel employ et Supprimer. Ajoutez deux contrleurs de tableaux intituls Employees et DeptPopUp. Ces oprations sont rcapitules la Figure 30.7. Le contrleur de tableau Employees doit tre en mode Entity et obtenir ses donnes depuis lentit Employee. Le contrleur de tableau DeptPopUp doit tre en mode Entity et prendre ses donnes partir de lentit Department. Ils doivent tous deux prparer automatiquement le contenu.

370

Cocoa

Figure 30.7
Linterface de base.

Le contrleur de tableau Employees est la cible des deux boutons. Le bouton Nouvel employ dclenche la mthode add:, tandis que le bouton Supprimer invoque remove:. Dnissez les liaisons recenses au Tableau 30.2.
Tableau 3.2 : Liaisons pour EmployeeView.nib

Objet CT1 Employees CT DeptPopUp Bouton Supprimer Colonne Prnom Colonne Nom Colonne Service Colonne Service Colonne Service

Liaison MOC2 MOC Enabled Value Value Content

Vers
Files Owner Files Owner

Cl du contrleur Chemin de cl
managedObjectContext managedObjectContext

CT Employees canRemove CT Employees arrangedObjects CT Employees arrangedObjects CT DeptPopUp arrangedObjects


deptName deptName firstName firstName

Content values CT DeptPopUp arrangedObjects Selected object CT Employees arrangedObjects

1. CT = Contrleur de tableau 2. MOC = Managed Object Context

Compilez et excutez lapplication. Vous devriez tre en mesure dajouter et de supprimer des employs. Vous devriez galement pouvoir xer le directeur dun service.

Chapitre 30

Relations Core Data

371

vnements et nextResponder
En gnral, les mthodes dvnements, comme mouseDown: et keyDown:, dnies dans NSResponder se contentent de rediriger lvnement vers le nextResponder. Ainsi, les vnements non traits traversent la chane des rpondeurs. Par exemple, lorsquun utilisateur slectionne une ligne dans une vue tableau et appuie sur la touche Suppr, lvnement remonte la chane des rpondeurs jusqu ce quil soit trait. Prenons en charge ce comportement dans la vue EmployeeView du projet Departments. Puisque NSViewController est une sous-classe de NSResponder, nous pouvons la placer dans la chane des rpondeurs de manire grer les vnements du clavier non traits. Ajoutez les lignes suivantes la n de displayView-Controller: dans MyDocument.m :
[box setContentView:v]; // Placer le contrleur de vue dans la chane des rpondeurs. [v setNextResponder:vc]; [vc setNextResponder:box]; }

Lorsque le contrleur de vue reoit une mthode keyDown:, il vrie sil sagit dune suppression. Dans lafrmative, il envoie le message remove: au contrleur de tableau. Ajoutez la mthode suivante dans EmployeeViewController.m :
// Grer la touche Suppr. - (void)keyDown:(NSEvent *)e { if ([e keyCode] == 51) { [employeeController remove:nil]; } else { [super keyDown:e]; } }

Nous avons besoin dune outlet nomme employeeController. Ajoutez-la dans EmployeeViewController.h :
IBOutlet NSArrayController *employeeController;

Ouvrez EmployeeView.nib. Faites un Contrle-clic sur Files Owner. Affectez le contrleur de tableau Employees loutlet employeeController. Compilez et excutez lapplication. Slectionnez un employ et appuyez sur la touche Suppr. Il doit disparatre.

31
Ramasse-miettes
Au sommaire de ce chapitre

Types de donnes non objets Exemple Instruments Pour les plus curieux : rfrences faibles

Tant que nous utilisons des objets Objective-C, le ramasse-miettes fonctionne comme nous souhaitons, sans que nous nous en proccupions. Cependant, si nous commenons invoquer malloc pour allouer des types de donnes C et des structures Core Foundation, nous devons tre un peu plus aviss. Lorsque le ramasse-miettes travaille, il recherche les objets inatteignables. Les objets dune application peuvent tre vus comme un graphe orient : cet objet connat cet objet, qui connat ces objets, qui connaissent ces autres objets. Le ramasse-miettes dbute avec les pointeurs sur la pile et les variables globales, puis parcourt ce graphe orient jusqu ce quil ait enregistr tous les objets "atteignables". Les objets inatteignables sont dsallous. La Figure 31.1 illustre le fonctionnement du ramasse-miettes. Avant quun objet ne soit dsallou par le ramasse-miettes, il reoit le message finalize. Normalement, nous navons pas besoin dimplmenter finalize, mais, si cela est ncessaire, voici un squelette de cette mthode :
- (void)finalize { ...Oprations de dernires minutes... [super finalize]; }

374

Cocoa

Figure 31.1
Objets atteignables.

Jusqu prsent, le code de cet ouvrage a t crit en "mode dual" : il fonctionne avec ou sans le ramasse-miettes. Dans ce chapitre et le suivant, le code sera crit pour fonctionner uniquement avec le ramasse-miettes. Il ne contiendra donc aucun appel retain ou release, pas plus que de mthode dealloc.

Types de donnes non objets


Types primitifs de C Comment le ramasse-miettes peut-il dsallouer un tampon dentiers lorsquil nest plus atteignable ? Il faut remplacer malloc() par une autre mthode :
int *tamponEntiers; tamponEntiers = NSAllocateCollectable(100 * sizeof(int), 0);

Lorsquun objet en "connat" un autre, nous disons quil possde une rfrence cet objet. Les rfrences peuvent tre "fortes" ou "faibles". Les rfrences fortes sont respectes par le ramasse-miettes. Il ignore les rfrences faibles. Par consquent, sil nexistait que des rfrences faibles sur tamponEntiers, il serait dsallou immdiatement par le ramasse-miettes.

Chapitre 31

Ramasse-miettes

375

Les variables dinstance de type objets sont, par dfaut, fortes. Les variables dinstance dautres types sont, par dfaut, faibles. Si nous voulons que la variable dinstance qui fait rfrence tamponEntiers soit forte, nous devons lindiquer explicitement :
_ _strong int *tamponEntiers;

Que reprsente le deuxime argument de NSAllocateCollectable ? Si la mmoire alloue contient uniquement des pointeurs considrs comme des rfrences fortes, le tampon doit tre marqu Scanned. Pour cela, nous passons NSScannedOption en second argument :
char **mots; mots = NSAllocateCollectable(100 * sizeof(char *), NSScannedOption);

Core Foundation Pour placer les structures de donnes de Core Foundation (CF) sous le contrle du ramasse-miettes, il existe une mthode trs pratique :
CFString *chaine = CFCreate... CFMakeCollectable(chaine);

nouveau, si la rfrence doit tre forte, nous devons lindiquer explicitement :


_ _strong CFStringRef chaine;

Exemple
Dans cet exemple, nous allons crer une classe Polynomial qui contient un tableau C de float et un CGColorRef. Polynomial se dessinera elle-mme, en utilisant CoreGraphics. Une vue PolynomialView afchera un tableau dobjet Polynomial (voir Figure 31.2).
Figure 31.2
Lapplication en cours dexcution.

376

Cocoa

Les classes Cocoa qui effectuent des tracs sont des enveloppes autour de CoreGraphics. Les structures de donnes employes dans CoreGraphics sont des types CF. Par consquent, si nous utilisons directement cette API de bas niveau, nous devons faire en sorte que les structures de donnes restent atteignables jusqu ce que nous nen ayons plus besoin. ce moment-l, elles seront dsalloues par le ramasse-miettes. Dans Xcode, crez un nouveau projet Cocoa Application nomm Polynomials. Ajoutez ce projet une classe Objective-C nomme Polynomial. La classe Polynomial possdera des rfrences fortes au tableau C et au CGColorRef. Modiez Polynomial.h :
#import <Cocoa/Cocoa.h> @interface Polynomial: NSObject { _ _strong CGFloat * terms; int termCount; _ _strong CGColorRef color; } - (float)valueAt:(float)x; - (void)drawInRect:(CGRect)b inContext:(CGContextRef)ctx; - (CGColorRef)color; @end

Les pointeurs sur le tableau C et le CGColorRef sont explicitement marqus comme des rfrences fortes. Cela empchera le ramasse-miettes de les dsallouer tant que nous en aurons besoin. Dans Polynomial.m, ces structures de donnes doivent tre cres de manire rester sous le contrle du ramasse-miettes :
#import "Polynomial.h" #import <QuartzCore/QuartzCore.h> // Nombres de segments tracs. #define HOPS (100) // Entier flottant alatoire entre 0 et 1. #define RANDFLOAT() (random() % 128 / 128.0) // Partie du graphique x-y qui sera dessine. static CGRect funcRect = {-20, -20, 40, 40}; @implementation Polynomial // Crer terms et color sous le contrle du ramasse-miettes. - (id)init { [super init]; // Crer une couleur. color = CGColorCreateGenericRGB(RANDFLOAT(), RANDFLOAT(), RANDFLOAT(), 0.5);

Chapitre 31

Ramasse-miettes

377

// La placer sous le contrle du ramasse-miettes. CFMakeCollectable(color); // Crer les coefficients du polynme entre -5 et 5. termCount = (random() % 3) + 2; terms = NSAllocateCollectable(termCount * sizeof(CGFloat), 0); // Leur affecter des valeurs alatoires. int i; for (i = 0; i < termCount; i++) { terms[i] = 5.0 - (random() % 100) / 10.0; } return self; } - (float)valueAt:(float)x { float result = 0; int i; for (i = 0; i < termCount; i++) { result = (result * x) + terms[i]; } return result; } // Effectuer le trac avec CoreGraphics. - (void)drawInRect:(CGRect)b inContext:(CGContextRef)ctx { NSLog(@"trac"); CGContextSaveGState(ctx); // Modifier lchelle et le dcalage du systme de coordonnes afin // que le trac dans funcRect remplisse la vue. CGAffineTransform tf = CGAffineTransformMake(b.size.width / funcRect.size.width, 0, 0, b.size.height / funcRect.size.height, b.size.width/2, b.size.height/2); // Appliquer la transformation affine au contexte graphique. CGContextConcatCTM(ctx, tf); CGContextSetStrokeColorWithColor(ctx, color); CGContextSetLineWidth(ctx, 0.4); float distance = funcRect.size.width / HOPS; float currentX = funcRect.origin.x; BOOL first = YES; while (currentX <= funcRect.origin.x + funcRect.size.width) { float currentY = [self valueAt:currentX]; if (first) { CGContextMoveToPoint(ctx, currentX, currentY); first = NO; } else { CGContextAddLineToPoint(ctx, currentX, currentY); } currentX += distance; } CGContextStrokePath(ctx); // Retirer la transformation affine. CGContextRestoreGState(ctx); }

378

Cocoa

- (CGColorRef)color { return color; } // Consigner la terminaison. - (void)finalize { NSLog(@"terminaison de %@", self); [super finalize]; } @end

Il faut faire bien attention aux points suivants :


m

Si nous oublions la directive _ _strong, le CGColor et le tableau C sont dsallous immdiatement par le ramasse-miettes. Le programme sarrte alors de manire trange et imprvisible. Si nous oublions dutiliser NSAllocateCollectable() pour crer le tableau C ou ne marquons pas le CGColor laide de CFMakeCollectable(), le ramasse-miettes ne nettoie jamais les zones de mmoire correspondantes. Lapplication souffre alors dune fuite de mmoire.

Crez prsent une sous-classe de NSView pour afcher les objets Polynomial. Nommez-la PolynomialView :
#import <Cocoa/Cocoa.h> @interface PolynomialView: NSView { NSMutableArray *polynomials; } - (IBAction)createNewPolynomial:(id)sender; - (IBAction)deleteRandomPolynomial:(id)sender; @end

Dans PolynomialView.m :
#import "PolynomialView.h" #import "Polynomial.h" #import <QuartzCore/QuartzCore.h> @implementation PolynomialView - (id)initWithFrame:(NSRect)frame { [super initWithFrame:frame]; polynomials = [[NSMutableArray alloc] init]; return self; } - (IBAction)createNewPolynomial:(id)sender { Polynomial *p = [[Polynomial alloc] init]; [polynomials addObject:p];

Chapitre 31

Ramasse-miettes

379

[self setNeedsDisplay:YES]; } - (IBAction)deleteRandomPolynomial:(id)sender { if ([polynomials count] == 0) { NSBeep(); return; } int i = random() % [polynomials count]; [polynomials removeObjectAtIndex:i]; [self setNeedsDisplay:YES]; } - (void)drawRect:(NSRect)rect { NSRect bounds = [self bounds]; [[NSColor whiteColor] set]; [NSBezierPath fillRect:bounds]; // Obtenir le contexte graphique. CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort]; CGRect cgBounds = *(CGRect *)&bounds; // Tracer les polynmes. for (Polynomial *p in polynomials) { [p drawInRect:cgBounds inContext:ctx]; } } @end

Ouvrez MainMenu.nib. Dposez deux boutons et une vue personnalise sur la fentre. Intitulez lun des boutons Ajouter un polynme et lautre, Supprimer un polynme. Fixez la classe de la vue personnalise PolynomialView (voir Figure 31.3).
Figure 31.3
Linterface de base.

380

Cocoa

Fixez la cible des deux boutons PolynomialView. Leur action respective doit tre createNewPolynomial: et deleteRandomPolynomial:. Avant de compiler lapplication, vriez que le ramasse-miettes est activ. Dans Xcode, double-cliquez sur la cible an dafcher linspecteur. Dans les paramtres de compilation, le ramasse-miettes doit tre requis pour toutes les congurations (voir Figure 31.4).
Figure 31.4
Activer le ramassemiettes.

Compilez et excutez lapplication. Lorsque vous supprimez des polynmes, vous devez voir sur la console apparatre le message de la mthode finalize de linstance dsalloue. Nous sommes prsent certains que les objets Polynomial sont correctement ramasss. Quen est-il des structures CGColor ? Pour cela, nous pouvons utiliser Instruments.

Chapitre 31

Ramasse-miettes

381

Instruments
Instruments est un outil pour lanalyse dun programme en cours dexcution. Cet outil dispose de nombreux plug-in, appels instruments, qui permettent dexaminer diffrents aspects, souvent lis aux performances, de lapplication. Dans notre exemple, nous allons utiliser linstrument ObjectAllocations pour surveiller la cration et la destruction des objets et des structures de donnes. Le lancement de Polynomials dans Instruments se fait laide de lentre de menu Run > Start with Performance Tool > Object Allocations dans Xcode (voir Figure 31.5).
Figure 31.5
Dmarrer Instruments.

Instruments, sur de nombreux points semblable GarageBand, est dmarr. Si nous cliquons sur le bouton Run (avec le centre rouge), lapplication est lance. En haut de la fentre dInstruments, un graphique reprsente la mmoire totale utilise par lapplication. La moiti infrieure de la fentre afche la liste des diffrentes structures de donnes et le nombre dinstances actuellement en mmoire. Crez quelques polynmes et suspendez lapplication. Recherchez Polynomial et CGColor dans la liste et ajoutez-les au graphique. Retirez les allocations totales (voir Figure 31.6). Poursuivez lexcution de lapplication. En ajoutant et en supprimant des polynmes, vous devriez constater que le nombre dobjets Polynomial et de structures CGColor (colonne Net #) augmente et baisse. Sil ne baisse pas, vous avez une fuite de mmoire.

382

Cocoa

Figure 31.6
Excution dans Instruments.

En cliquant sur Polynomial dans la colonne Category, vous pouvez inspecter chaque instance de Polynomial et savoir comment elles ont t cres (voir Figure 31.7).
Figure 31.7
Examiner les instances de Polynomial.

Comment pouvons-nous vrier les fuites lies aux tableaux C ? Malheureusement, les tableaux C que nous avons crs sont de petites allocations gnrales, et il en existe une foultitude dans une application en cours dexcution. Les rechercher sera trs difcile. Voici une astuce stupide qui fonctionne : effectuer des allocations volumineuses et

Chapitre 31

Ramasse-miettes

383

dune taille prcise. Modiez le code an dallouer de lespace pour 100 nombres en virgule ottante (400 octets en mode 32 bits, 800 octets en mode 64 bits) :
terms = NSAllocateCollectable(100 * sizeof(CGFloat), NSScannedOption);

Cette allocation sera afche dans Instruments sous lintitul GeneralBlock-400 (ou GeneralBloc-800 en mode 64 bits). Vous pouvez prsent les placer sur le graphique. Le ramasse-miettes les collecte-t-il ? Instruments est un outil trs performant et ses possibilits sont nombreuses. Lorsque vous serez en train doptimiser les performances de votre application, consultez la documentation dInstruments fournie par Apple.

Pour les plus curieux : rfrences faibles


Parfois, nous voudrons quun pointeur pointe sur un objet tant quil existe, mais sans que ce pointeur empche le ramasse-miettes de dsallouer lobjet. Dans ce cas, nous devons employer une rfrence faible :
_ _weak NSFont *policeFavorite;

Le ramasse-miettes ne sera pas empch de dsallouer lobjet, mme si policeFavorite pointe sur cet objet. Si policeFavorite pointe sur un objet lorsque celui-ci est dsallou, policeFavorite est automatiquement x nil. Certaines collections, comme NSMapTable, NSHashTable et NSPointerArray, contiennent galement des rfrences faibles sur des objets. Par ailleurs, lorsque le ramasse-miettes est activ dans une application, le centre de notication possde des rfrences faibles sur les observateurs.

Exercice : faire des btises


Crez des fuites de mmoire laide du tableau C et des structures CGColor lorsque vous supprimez un polynme. Observez les fuites dans Instruments. Ensuite, corrigez-les. Plantez lapplication en faisant en sorte que le ramasse-miettes dsalloue prmaturment le tableau C et les structures CGColor. Lancez le dbogueur et observez le crash rsultant. Corrigez les erreurs.

32
Bases de lanimation
Au sommaire de ce chapitre
Crer un CALayer Utiliser CALayer et CAAnimation

Mac OS X utilise de plus en plus OpenGL pour exploiter la puissance des processeurs graphiques modernes. An de permettre aux programmeurs de bncier de ces possibilits, Apple a cr CALayer dans Mac OS X 10.5. CALayer peut tre vu comme un tampon dans lequel nous pouvons dessiner. Une fois son rendu effectu, il peut tre dplac, redimensionn et redessin par le processeur graphique des vitesses tonnantes. Comme les vues, les calques sont organiss en hirarchie. Une vue peut tre couverte par un calque, et ce calque peut contenir des sous-calques. Si nous disposons du CALayer, qui peut tre manipul des vitesses surprenantes, il nous faut quelque chose pour piloter le processus. Cest le rle de CAAnimation. NSAnimationContext peut servir regrouper et synchroniser plusieurs animations. titre dexemple, nous partirons du programme de dessin de polynmes du Chapitre 31 et dplacerons chaque polynme sur son propre CALayer. Ensuite, nous pourrons dplacer grande vitesse les calques sur la vue (voir Figure 32.1).

386

Cocoa

Figure 32.1
Graphe dobjets.

Crer un CALayer
Ouvrez le projet Polynomials dans Xcode. Puisque les classes danimation font partie du framework QuartzCore, faites un Contrle-clic sur Linked Frameworks an dajouter le framework /System/Library/Frameworks/QuartzCore.framework lapplication (voir Figure 32.2).

Figure 32.2
Ajouter un framework.

Chapitre 32

Bases de lanimation

387

Ouvrez le chier MainMenu.nib. Dans linspecteur Effects, cochez la case qui correspond PolynomialView an de ladosser un CALayer (voir Figure 32.3). Nous disons que la vue veut un CALayer.

Figure 32.3
La vue PolynomialView veut un CALayer.

Ajoutez un bouton intitul Feu la fentre (voir Figure 32.4).


Figure 32.4
Ajouter un bouton.

388

Cocoa

Le bouton Feu fera disparatre tous les polynmes de la vue. Un second clic les fera rapparatre. Nous dclarons la mthode associe dans PolynomialView.h. Nous avons galement besoin dune valeur boolenne pour savoir si les polynmes ont dj disparu de lcran :
#import <Cocoa/Cocoa.h>

@interface PolynomialView: NSView { NSMutableArray *polynomials; BOOL blasted; } - (IBAction)createNewPolynomial:(id)sender; - (IBAction)deleteRandomPolynomial:(id)sender; - (IBAction)blastem:(id)sender; @end

Dans Interface Builder, connectez le nouveau bouton la mthode blastem:. Dans cet exercice, nous allons laisser les calques soccuper du trac. Dans PolynomialView.m, modiez la mthode drawRect: an de crer un fond blanc :
- (void)drawRect:(NSRect)rect { NSRect bounds = [self bounds]; [[NSColor whiteColor] set]; [NSBezierPath fillRect:bounds]; }

Crez un point dentre pour blastem: dans PolynomialView.m :


#import "PolynomialView.h" #import "Polynomial.h" #import <QuartzCore/QuartzCore.h> #define MARGIN (10) @implementation PolynomialView - (id)initWithFrame:(NSRect)frame { [super initWithFrame:frame]; polynomials = [[NSMutableArray alloc] init]; blasted = NO; return self; } - (IBAction)blastem:(id)sender { }

Mme si elle ne fait pas grand-chose, compilez et excutez lapplication. Vous obtenez une belle vue blanche.

Chapitre 32

Bases de lanimation

389

Utiliser CALayer et CAAnimation


Au calque de base, nous allons prsent ajouter des calques contenant les polynmes. Tout dabord, nous devons crer une mthode qui dterminera un point alatoire hors cran. Nous dplacerons lorigine du calque sur ce point alatoire. Ajoutez la mthode suivante dans PolynomialView.m :
- (NSPoint)randomOffViewPosition { NSRect bounds = [self bounds]; float radius = hypot(bounds.size.width, bounds.size.height); float angle = 2.0 * M_PI * (random() % 360 / 360.0); NSPoint p; p.x = radius * cos(angle); p.y = radius * sin(angle); return p; }

Elle est dclare dans PolynomialView.h. Lorsque lutilisateur cre un nouveau polynme, nous crons galement un calque, dont le dlgu sera lobjet Polynomial. Dans Polynomial.m, nous devons donc implmenter la mthode qui sera invoque lorsque le calque voudra que son dlgu le dessine :
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { CGRect cgb = [layer bounds]; [self drawInRect:cgb inContext:ctx]; }

Dans PolynomialView.m, nous devons rcrire la mthode qui cre de nouveaux polynmes et les anime sur la vue en partant du point alatoire :
- (IBAction)createNewPolynomial:(id)sender { // Crer une instance de Polynomial. Polynomial *p = [[Polynomial alloc] init]; [polynomials addObject:p]; // Crer un calque. CALayer *layer = [CALayer layer]; CGRect b = [[self layer] bounds]; b = CGRectInset(b, MARGIN, MARGIN); [layer setAnchorPoint:CGPointMake(0,0)]; [layer setFrame:b]; [layer setCornerRadius:12]; [layer setBorderColor:[p color]]; [layer setBorderWidth:3.5];

390

Cocoa

// Linstance de Polynomial se chargera du trac. [layer setDelegate:p]; // Ajouter le nouveau calque au calque de base de la vue. [[self layer] addSublayer:layer]; // Effectuer le rendu du calque. [layer display]; // Crer une animation qui dplace le calque sur lcran en une seconde. CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"]; [anim setFromValue: [NSValue valueWithPoint:[self randomOffViewPosition]]]; [anim setToValue: [NSValue valueWithPoint:NSMakePoint(MARGIN,MARGIN)]]; [anim setDuration:1.0]; CAMediaTimingFunction *f = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; [anim setTimingFunction:f]; // Lancer lanimation. [layer addAnimation:anim forKey:@"whatever"]; }

Compilez et excutez lapplication. Pour le moment, cliquez simplement sur le bouton Ajouter un polynme. De jolis polynmes arrivent-ils sur lcran ? Supprimer des polynmes Nous devons prsent revoir la mthode deleteRandomPolynomial:. Il nous faut deux mthodes : une premire qui dmarre lanimation et une seconde qui retire le calque une fois lanimation termine :
- (IBAction)deleteRandomPolynomial:(id)sender { // Obtenir le tableau de CALayer qui reprsente les polynmes. NSArray *polynomialLayers = [[self layer] sublayers]; // Y a-t-il des polynmes supprimer? if ([polynomialLayers count] == 0) { NSBeep(); return; } // Choisir un calque alatoire. int i = random() % [polynomialLayers count]; CALayer *layerToPull = [polynomialLayers objectAtIndex:i]; // Choisir un point vers lequel le dplacer. NSPoint randPoint = [self randomOffViewPosition]; // Crer lanimation qui pilotera le dplacement en dehors de lcran. CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];

Chapitre 32

Bases de lanimation

391

// Lanimation nous permet de placer ce que nous souhaitons dans son // dictionnaire. la fin de lanimation, nous voudrons connatre // le polynme qui a t dplac en dehors de lcran. [anim setValue:layerToPull forKey:@"representedPolynomialLayer"]; [anim setFromValue: [NSValue valueWithPoint:NSMakePoint(MARGIN,MARGIN)]]; [anim setToValue: [NSValue valueWithPoint:randPoint]]; [anim setDuration:1.0]; CAMediaTimingFunction *f = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; [anim setTimingFunction:f]; // Nous avons besoin dune fonction de rappel pour la fin de lanimation. [anim setDelegate:self]; [layerToPull addAnimation:anim forKey:@"whatever"]; // Pendant lanimation, la position est temporaire. Sans la ligne // suivante, le polynme supprim clignote avant dtre supprim. [layerToPull setPosition:CGPointMake(randPoint.x, randPoint.y)]; } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { CALayer *layerToPull = [anim valueForKey:@"representedPolynomialLayer"]; Polynomial *p = [layerToPull delegate]; [polynomials removeObjectIdenticalTo:p]; [layerToPull removeFromSuperlayer]; }

Construisez et compilez lapplication. Vous devez pouvoir supprimer des polynmes. Dplacer plusieurs calques simultanment La mthode blastem: va dplacer plusieurs calques la fois. Pour regrouper des animations, nous utilisons NSAnimationContext :
- (IBAction)blastem:(id)sender { [NSAnimationContext beginGrouping]; [[NSAnimationContext currentContext] setDuration:3.0]; NSArray *polynomialLayers = [[self layer] sublayers]; for (CALayer *layer in polynomialLayers) { CGPoint p; if (blasted) { p = CGPointMake(MARGIN, MARGIN); } else { NSPoint r = [self randomOffViewPosition]; // Convertir le NSPoint en CGPoint. p = *(CGPoint *)&r; }

392

Cocoa

[layer setPosition:p]; } [NSAnimationContext endGrouping]; blasted =!blasted; }

Compilez et excutez lapplication. Cliquez sur le bouton Feu. Pas mal, non ? Essayez de redimensionner la fentre. Redimensionner et redessiner les calques Pour notre dernire amlioration, nous allons redimensionner le calque de base, mais redimensionner et redessiner les calques des polynmes uniquement lorsque le redimensionnement de la fentre est termin :
- (void)resizeAndRedrawPolynomialLayers { CGRect b = [[self layer] bounds]; b = CGRectInset(b, MARGIN, MARGIN); NSArray *polynomialLayers = [[self layer] sublayers]; [NSAnimationContext beginGrouping]; [[NSAnimationContext currentContext] setDuration:0]; for (CALayer *layer in polynomialLayers) { b.origin = [layer frame].origin; [layer setFrame:b]; [layer setNeedsDisplay]; } [NSAnimationContext endGrouping]; } - (void)setFrameSize:(NSSize)newSize { [super setFrameSize:newSize]; if (![self inLiveResize]) { [self resizeAndRedrawPolynomialLayers]; } } - (void)viewDidEndLiveResize { [self resizeAndRedrawPolynomialLayers]; }

Compilez et excutez lapplication. Redimensionnez la fentre. CALayer Dans cet exercice, nous dessinons explicitement sur le CGContext du CALayer dans notre classe Polynomial. Ce mcanisme nous permet deffectuer tous les tracs imaginables sur le CALayer.

Chapitre 32

Bases de lanimation

393

Cependant, il est plus frquent de vouloir simplement manipuler quelques aspects classiques communs :
m m m m

une image ; la couleur darrire-plan ; larrondi des angles ; un ltre dimage dans lequel faire passer le contenu du calque.

Les instances de CALayer peuvent se charger de toutes ces oprations elles-mmes, sans passer par un dlgu. Une fois encore, les ingnieurs dApple respectent leurs promesses de faciliter les choses classiques et de rendre possibles les choses plus rares. Il existe des sous-classes de CALayer pour faciliter certains dessins :
m

Lafchage dun texte sur un calque est plus facile si ce calque est une instance de CATextLayer. Effectuer des appels au OpenGL sur un calque est plus facile si ce calque est une sous-classe de CAOpenGLLayer. Le calque de base dune vue est une instance de _NSViewBackingLayer (une classe non publique) qui sait dessiner le contenu de la vue sur elle-mme. Dans cet exercice, le calque auquel sadosse la vue se peint simplement en blanc. Cest la raison du code de la mthode drawRect: de la vue.

33
Application Cocoa/OpenGL simple
Au sommaire de ce chapitre
Utiliser NSOpenGLView crire lapplication

Ce chapitre na pas pour prtention de vous enseigner OpenGL ; pour cela, tournezvous vers dautres ouvrages, comme The OpenGL Programming Guide. Il se contente dexpliquer comment effectuer des dessins avec OpenGL dans une application crite avec Cocoa. Comme pour tous les autres tracs graphiques dans Cocoa, le rendu OpenGL se fait dans une vue. Jusqu prsent, nous avons utilis des vues bases sur NSGraphicsContext pour raliser les tracs avec Quartz, via NSImage, NSBezierPath et NSAttributedString.

Utiliser NSOpenGLView
NSOpenGLView est une sous-classe de NSView avec un contexte graphique OpenGL. Tout comme nous devions verrouiller le focus sur une vue pour dessiner avec Quartz, le contexte graphique OpenGL doit tre actif pour que les commandes de dessin OpenGL aient un effet.

Voici quelques-unes des mthodes les plus importantes de NSOpenGLView :


- (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat *)format

Linitialiseur dsign.
- (NSOpenGLContext*)openGLContext

Retourne le contexte OpenGL de la vue.

396

Cocoa

- (void)reshape

Appele lorsque la vue est redimensionne. Le contexte OpenGL est actif au moment de linvocation de cette mthode.
- (void)drawRect:(NSRect)r

Appele lorsque la vue doit tre actualise. Le contexte OpenGL est actif au moment de linvocation de cette mthode.

crire lapplication
La Figure 33.1 illustre lapplication que nous allons dvelopper.
Figure 33.1
Lapplication termine.

Crez un nouveau projet dapplication Cocoa nomm Gliss (pour "GL Bliss", ou "GL batitude"). Dans le menu Project, slectionnez Add to Project... pour ajouter au projet les frameworks OpenGL.framework et GLUT.framework, tous deux dans /Systme/Bibliothque/ Frameworks/. Nous utiliserons non pas le modle dvnements GLUT, mais uniquement quelques fonctions commodes. Crez une nouvelle classe Objective-C nomme GlissView. Dans GlissView.h, modiez la superclasse et dclarez une outlet ainsi quune action :
#import <Cocoa/Cocoa.h> @interface GlissView: NSOpenGLView { IBOutlet NSMatrix *sliderMatrix; } - (IBAction)changeParameter:(id)sender; @end

Chapitre 33

Application Cocoa/OpenGL simple

397

Agencer linterface Ouvrez MainMenu.nib. partir de la bibliothque, faites glisser un NSOpenGLView (sous Cocoa > Views & Cells > Data Views) sur la fentre (voir Figure 33.2).
Figure 33.2
Dposer un NSOpenGLView.

Dans linspecteur Identity, xez la classe de la vue GlissView (voir Figure 33.3).
Figure 33.3
Fixer la classe.

Dposez sur la fentre un NSSlider congur en mode continu. Dans le menu Layout, choisissez Embed objects in > Matrix. Dans linspecteur, congurez la matrice en mode Tracking et donnez-lui trois colonnes (voir Figure 33.4).

398

Cocoa

Figure 33.4
Matrice de curseurs.

Affectez GlissView comme cible de la matrice et changeParameter: pour son action. Faites pointer loutlet sliderMatrix de GlissView sur la matrice. Assurez-vous de crer les connexions dans les deux directions. Le premier curseur contrlera la coordonne X de la source lumineuse. Donnez-lui une plage de 4 4, avec la valeur initiale 1. Sa balise doit tre 0. La Figure 33.5 prsente ltat correspondant de linspecteur.
Figure 33.5
Fixer les limites, la valeur initiale et la balise du premier curseur.

Le deuxime curseur contrlera langle de vue de la scne. Donnez-lui une plage de 4 4, avec la valeur initiale 0. Sa balise doit tre 1.

Chapitre 33

Application Cocoa/OpenGL simple

399

Le troisime curseur contrlera lloignement de la scne. Donnez-lui une plage de 0,3 5, avec la valeur initiale 4. Sa balise doit tre 2. Slectionnez GlissView. Dans linspecteur Attributes, congurez une profondeur de 16 bits (voir Figure 33.6).
Figure 33.6
Crer un tampon de profondeur de 16 bits.

Dans linspecteur Size, congurez GlissView pour que sa taille suive celle de la fentre. Inspectez le NSMatrix. Congurez-le pour dimensionnement automatique de ses cellules. Dans linspecteur Size, attachez-le au bord droit de la fentre (voir Figure 33.7). Enregistrez le chier nib.
Figure 33.7
Linspecteur Size de la matrice.

400

Cocoa

crire le code Modiez GlissView.h :


#import <Cocoa/Cocoa.h> @interface GlissView: NSOpenGLView { IBOutlet NSMatrix *sliderMatrix; float lightX, theta, radius; int displayList; } - (IBAction)changeParameter:(id)sender; @end

Modiez galement GlissView.m :


#import "GlissView.h" #import <GLUT/glut.h> #define LIGHT_X_TAG 0 #define THETA_TAG 1 #define RADIUS_TAG 2 @implementation GlissView - (void)prepare { NSLog(@"prepare"); // Le contexte OpenGL doit tre actif pour que cette fonction ait un effet. NSOpenGLContext *glcontext = [self openGLContext]; [glcontext makeCurrentContext]; // Configurer la vue. glShadeModel(GL_SMOOTH); glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); // Ajouter un clairage ambiant. GLfloat ambient[] = {0.2, 0.2, 0.2, 1.0}; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); // Initialiser la source lumineuse. GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0}; glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); // Lteindre. glEnable(GL_LIGHT0); // Fixer les proprits des matriaux sous un clairage ambiant. GLfloat mat[] = {0.1, 0.1, 0.7, 1.0}; glMaterialfv(GL_FRONT, GL_AMBIENT, mat); // Fixer les proprits des matriaux sous un clairage diffus. glMaterialfv(GL_FRONT, GL_DIFFUSE, mat); }

Chapitre 33

Application Cocoa/OpenGL simple

401

- (id)initWithCoder:(NSCoder *)c { self = [super initWithCoder:c]; [self prepare]; return self; } // Appele lorsque la vue est redimensionne. - (void)reshape { NSLog(@"reshape"); // Ajuster lespace de la fentre, qui est en pixels. NSRect baseRect = [self convertRectToBase:[self bounds]]; // Le rsultat est prsent compatible avec glViewport(). glViewport(0, 0, baseRect.size.width, baseRect.size.height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, baseRect.size.width/baseRect.size.height, 0.2, 7); } - (void)awakeFromNib { [self changeParameter:self]; } - (IBAction)changeParameter:(id)sender { lightX = [[sliderMatrix cellWithTag:LIGHT_X_TAG] floatValue]; theta = [[sliderMatrix cellWithTag:THETA_TAG] floatValue]; radius = [[sliderMatrix cellWithTag:RADIUS_TAG] floatValue]; [self setNeedsDisplay:YES]; } - (void)drawRect:(NSRect)r { // Effacer larrire-plan. glClearColor (0.2, 0.4, 0.1, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Fixer le point de vue. glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(radius * sin(theta), 0, 0, 0, 0, 0, 1, 0);

radius * cos(theta),

// Positionner lclairage. GLfloat lightPosition[] = {lightX, 1, 3, 0.0}; glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); if (!displayList) { displayList = glGenLists(1); glNewList(displayList, GL_COMPILE_AND_EXECUTE);

402

Cocoa

// Tracer le contenu. glTranslatef(0, 0, 0); glutSolidTorus(0.3, 0.9, 35, 31); glTranslatef(0, 0, -1.2); glutSolidCone(1, 1, 17, 17); glTranslatef(0, 0, 0.6); glutSolidTorus(0.3, 1.8, 35, 31); glEndList(); } else { glCallList(displayList); } // Envoyer lcran. glFinish(); } @end

Les appels OpenGL sont rpartis dans trois mthodes : prepare, tous les appels envoyer initialement, reshape, tous les appels envoyer lorsque la vue est redimensionne, et drawRect:, tous les appels envoyer chaque fois que la vue doit tre actualise. Compilez et excutez lapplication.

34
NSTask
Au sommaire de ce chapitre

Multithread et multitraitement ZIPspector Lectures asynchrones iPing

Chaque application que nous avons cre est en ralit un rpertoire et, quelque part dans ce rpertoire se trouve un chier excutable. Pour lancer un chier excutable sur une machine Unix telle que le Mac, un processus est cr et le nouveau processus excute le code contenu dans ce chier. De nombreux excutables sont des outils en ligne de commande et certains sont trs pratiques. Ce chapitre explique comment excuter des outils en ligne de commande partir dune application Cocoa, laide de la classe NSTask.
NSTask est une enveloppe, trs simple demploi, autour des fonctions Unix fork() et exec(). Il suft de lui indiquer le chemin dun excutable et de le lancer. De nombreux processus lisent des donnes partir de lentre standard et crivent des donnes sur la sortie et lerreur standard. Une application peut utiliser NSTask pour attacher des tubes (pipes) an de transfrer des donnes vers et depuis un processus externe. Les tubes sont reprsents par la classe NSPipe.

404

Cocoa

Multithread et multitraitement
Le multithread est trs la mode car il permet de tirer prot de la prsence de plusieurs processeurs et des processeurs plusieurs curs. Le multithread peut galement garantir que lapplication continue ragir rapidement pendant quelle effectue dautres traitements en arrire-plan. Cependant, la programmation multithread nest pas simple : les activits dun thread crasent souvent les donnes utilises par un autre thread. Le multitraitement nous permet de bncier des avantages de la programmation multithread, sans les maux de tte associs. Autrement dit, au lieu de crer un nouveau thread pour effectuer une opration, nous crons simplement un nouveau processus. Voici quelques avantages du multitraitement par rapport au multithread.
m

La rcriture des fonctions de nombreux outils en ligne de commande demanderait beaucoup de temps. Si le processus externe contient une fuite de mmoire, le systme dexploitation se chargera du nettoyage notre place lorsque ce processus se terminera. Le processus externe peut sexcuter sous un compte dutilisateur diffrent de celui de lapplication. Par consquent, il peut disposer dautorisations totalement diffrentes de celles de lapplication.

Mme sil nest pas aussi attrayant que le multithread, le multitraitement est probablement plus utile dans la vie relle.

ZIPspector
Nous pouvons utiliser loutil /usr/bin/zipinfo pour examiner le contenu dun chier zip. Prenez un chier zip sur votre machine et excutez zipinfo dans Terminal (-1 est tiret-un, non tiret-lettre l) :
# /usr/bin/zipinfo -1 /Users/aaron/monfichier.zip fichierextra.rtf fichiersuper.txt magnifique.pdf

Nous allons crer une application qui utilise zipinfo (voir Figure 34.1). Elle devra envoyer des arguments et lire la sortie standard du processus.

Chapitre 34

NSTask

405

Figure 34.1
Lapplication termine.

Dans Xcode, crez un nouveau projet de type Cocoa Document-based Application. Nommez-le ZIPspector. Ce programme afche le contenu de chiers zip, mais ne permet pas de les modier. Dans le panneau Info de la cible, congurez ZIPspector en tant que visualiseur des chiers ayant lUTI com.pkware.zip-archive (voir Figure 34.2). Il sagit dun UTI dni par le systme, qui connat lextension, licne et les autres informations des chiers zip.
Figure 34.2
Fixer lUTI.

406

Cocoa

Dans MyDocument.h, nous crons des outlets pour un NSTableView et un NSArray qui contiendra les noms des chiers prsents dans le chier zip :
@interface MyDocument: NSDocument { IBOutlet NSTableView *tableView; NSArray *filenames; } @end

Ouvrez MyDocument.nib. Ajoutez une vue tableau dans la fentre et congurez-la avec une colonne non modiable intitule Fichiers. Faites un Contrle-clic sur la vue tableau pour afcher son panneau des connexions. Faites pointer loutlet dataSource sur Files Owner (voir Figure 34.3).
Figure 34.3
Fixer loutlet dataSource.

Faites un Contrle-clic sur Files Owner pour afcher son panneau des connexions et xez son outlet tableView (voir Figure 34.4). Dans MyDocument.m, nous rednissons readFromURL:ofType:error: de manire crer un NSTask qui excute zipinfo. Nous crons galement un NSPipe et le connectons la sortie standard ( standardOut) de NSTask (voir Figure 34.5).

Chapitre 34

NSTask

407

Figure 34.4
Fixer loutlet tableView.

Figure 34.5
Graphe dobjets.

NSTask launchPath = "/usr/bin/zipinfo" standardOutput

NSFileHandle

leHandleForReading NSPipe

Voici le code :
- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError { // Nom du fichier passer zipinfo. NSString *filename = [absoluteURL path]; // Prparer un objet NSTask. NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@"/usr/bin/zipinfo"]; NSArray *args = [NSArray arrayWithObjects:@"-1", filename, nil]; [task setArguments:args];

408

Cocoa

// Crer le tube partir duquel se fera la lecture. NSPipe *outPipe = [[NSPipe alloc] init]; [task setStandardOutput:outPipe]; [outPipe release]; // Dmarrer le processus. [task launch]; // Lire la sortie. NSData *data = [[outPipe fileHandleForReading] readDataToEndOfFile]; // Sassurer que la tche sest termine normalement. [task waitUntilExit]; int status = [task terminationStatus]; [task release]; // Vrifier le code dtat. if (status!= 0) { if (outError) { NSDictionary *eDict = [NSDictionary dictionaryWithObject:@"zipinfo failed" forKey:NSLocalizedFailureReasonErrorKey]; *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:0 userInfo:eDict]; } return NO; } // Convertir en chane de caractres. NSString *aString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; // Relcher les anciens noms de fichiers. [filenames release]; // Dcomposer la chane en lignes. filenames = [[aString componentsSeparatedByString:@"\n"] retain]; NSLog(@"noms de fichiers = %@", filenames); // Relcher la chane. [aString release]; // En cas dinversion. [tableView reloadData]; return YES; }

prsent, nous avons besoin des mthodes de source de donnes de la vue tableau :
- (int)numberOfRowsInTableView:(NSTableView *)v { return [filenames count]; }

Chapitre 34

NSTask

409

- (id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)tc row:(NSInteger)row { return [filenames objectAtIndex:row]; }

Lapplication noffre pas de fonction denregistrement. Nous pouvons donc supprimer la mthode dataOfType:error:. Dans le chier MainMenu.nib, nous pouvons galement supprimer les entres de menu qui concernent lenregistrement. Compilez et excutez lapplication. Vous devriez tre en mesure de visualiser le contenu de nimporte quel chier zip. Aucun document sans titre napparatra car il sagit dun visualiseur. Vous devez ouvrir un chier .zip existant.

Lectures asynchrones
Nous lavons mentionn au Chapitre 24, la boucle dexcution reprsente lobjet qui attend larrive dvnements. Il peut sagir dvnements du clavier, de la souris ou de minuteries. Ils constituent tous des sources de donnes pour la boucle dexcution. Nous pouvons galement transformer un chier en source de donnes pour la boucle dexcution. Dans cette section, nous allons crer un processus qui gnre sporadiquement des donnes. Nous connecterons un tube la sortie standard, mais, au lieu de tenter de lire immdiatement toutes les donnes partir du descripteur de chiers, nous les lirons en arrire-plan et signalerons lorsque des donnes sont prtes. Pour savoir si nous pouvons crer une connexion IP avec une autre machine, nous utilisons /sbin/ping. Essayez cet outil dans Terminal :
$ /sbin/ping -c10 www.bignerdranch.com PING www.bignerdranch.com (69.39.89.150): 56 data bytes 64 bytes from 69.39.89.150: icmp_seq=0 ttl=50 time=35.579 64 bytes from 69.39.89.150: icmp_seq=1 ttl=50 time=35.099 64 bytes from 69.39.89.150: icmp_seq=2 ttl=50 time=34.546 64 bytes from 69.39.89.150: icmp_seq=3 ttl=50 time=35.495 64 bytes from 69.39.89.150: icmp_seq=4 ttl=50 time=35.685 64 bytes from 69.39.89.150: icmp_seq=5 ttl=50 time=35.667 64 bytes from 69.39.89.150: icmp_seq=6 ttl=50 time=36.435 64 bytes from 69.39.89.150: icmp_seq=7 ttl=50 time=52.296 64 bytes from 69.39.89.150: icmp_seq=8 ttl=50 time=36.142 64 bytes from 69.39.89.150: icmp_seq=9 ttl=50 time=36.188

ms ms ms ms ms ms ms ms ms ms

--- www.bignerdranch.com ping statistics --10 packets transmitted, 10 packets received, 0% packet loss round-trip min/avg/max/stddev = 34.546/37.313/52.296/5.021 ms

Pour arrter le programme, appuyez sur Contrle+C. Il reoit alors un signal sigint, afche des statistiques lcran et se termine.

410

Cocoa

iPing
Nous allons crire une application Cocoa qui utilise NSTask pour excuter ping (voir Figure 34.6).
Figure 34.6
Lapplication termine.

Dans Xcode, crez un nouveau projet de type Cocoa Application nomm iPing. Ajoutez une nouvelle classe Objective-C nomme AppController. Elle a besoin de deux outlets, dune variable pour le NSTask et dune action :
@interface AppController: NSObject { IBOutlet NSTextView *outputView; IBOutlet NSTextField *hostField; IBOutlet NSButton *startButton; NSTask *task; NSPipe *pipe; } - (IBAction)startStopPing:(id)sender; @end

Ouvrez MainMenu.nib et dposez une vue texte, un champ de texte et un bouton sur la fentre. Le bouton doit tre congur en mode Toggle. Son intitul est Dmarrer Ping et son intitul alternatif, Arrter Ping (voir Figure 34.7). Faites glisser un NSObject depuis la bibliothque. Dans linspecteur Identity, xez sa classe AppController (voir Figure 34.8).

Chapitre 34

NSTask

411

Figure 34.7
Attributs du bouton.

Figure 34.8
Crer AppController.

AppController doit tre la cible du bouton, avec laction startStopPing:. Les outlets outputView, hostField et startButton doivent pointer, respectivement, sur la vue texte, le champ de texte et le bouton (voir Figure 34.9).

412

Cocoa

hostField leHandleForReading startButton outputView target pipe standardOutput

task
Figure 34.9
Graphe dobjets.

launchPath = "/sbin/ping"

Dans AppController.m, nous implmentons startStopPing: :


- (IBAction)startStopPing:(id)sender { // La tche est-elle en cours dexcution? if (task) { [task interrupt]; } else { task = [[NSTask alloc] init]; [task setLaunchPath:@"/sbin/ping"]; NSArray *args = [NSArray arrayWithObjects:@"-c10", [hostField stringValue], nil]; [task setArguments:args]; // Relcher lancien tube. [pipe release]; // Crer un nouveau tube. pipe = [[NSPipe alloc] init]; [task setStandardOutput:pipe]; NSFileHandle *fh = [pipe fileHandleForReading]; NSNotificationCenter *nc; nc = [NSNotificationCenter defaultCenter]; [nc removeObserver:self]; [nc addObserver:self selector:@selector(dataReady:)

Chapitre 34

NSTask

413

name:NSFileHandleReadCompletionNotification object:fh]; [nc addObserver:self selector:@selector(taskTerminated:) name:NSTaskDidTerminateNotification object:task]; [task launch]; [outputView setString:@""]; [fh readInBackgroundAndNotify]; } }

Pendant que la tche sexcute, le descripteur de chier envoie des modications si des donnes sont disponibles. Implmentez la mthode invoque :
- (void)appendData:(NSData *)d { NSString *s = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding]; NSTextStorage *ts = [outputView textStorage]; [ts replaceCharactersInRange:NSMakeRange([ts length], 0) withString:s]; [s release]; } - (void)dataReady:(NSNotification *)n { NSData *d; d = [[n userInfo] valueForKey:NSFileHandleNotificationDataItem]; NSLog(@"dataReady:%d bytes", [d length]); if ([d length]) { [self appendData:d]; } // Si la tche est en cours dexcution, recommencer lire. if (task) [[pipe fileHandleForReading] readInBackgroundAndNotify]; }

Lorsque le processus est termin, un nettoyage simpose :


- (void)taskTerminated:(NSNotification *)note { NSLog(@"taskTerminated:"); [task release]; task = nil; [startButton setState:0]; }

Compilez et excutez lapplication.

414

Cocoa

Exercice : chiers .tar et .tgz


Loutil zipinfo afche une liste des chiers contenus dans un chier zip. Il est possible dobtenir la mme chose pour des chiers tar laide de la commande tar :
# /usr/bin/tar tf MesFichiers.tar

Si le chier tar est galement compress, il faut ajouter loption z :


# /usr/bin/tar tzf MesFichiers.tgz

tendez ZIPspector pour quil prenne galement en charge les chiers .tar et .tgz.

35
Final
Lorsque je donne un cours, je termine toujours par les messages suivants :
m

Les connaissances que vous avez acquises tout au long de ce cours ne coulent pas de source. Vous avez appris beaucoup de choses. Soyez-en er. La seule manire de consolider ce que vous avez appris consiste crire des applications. Plus tt vous commencerez, plus facile ce sera. Vous avez encore beaucoup apprendre, mais vous avez franchi le premier col de la courbe dapprentissage. partir de maintenant, tout sera plus facile. Une fois encore, la seule manire de progresser consiste pratiquer. En tant quorateur, je suis disponible pour des mariages, des soires, des bar mitzvahs, ainsi que nimporte quel autre vnement. Je donne galement des cours de cinq jours au Big Nerd Ranch. Pour connatre les plannings, visitez le site web du Big Nerd Ranch (www.bignerdranch.com/).

Je fournis galement une liste de ressources qui permettront de rpondre aux questions lorsquelles se poseront. Comme pour tout sujet ayant trait la programmation, les rponses se trouvent parpilles dans la documentation en ligne, les sites web et les listes de diffusion.
m

Si vous avez une question concernant Cocoa, commencez par consulter la documentation de rfrence. Toutes les classes, les protocoles, les fonctions et les constantes sy trouvent. Regardez dans /Developer/Documentation/Cocoa/Reference/. Si vous avez une question concernant Objective-C, commencez par consulter la documentation de rfrence de ce langage. Utilisez le menu Help de Xcode pour accder cette documentation.

416

Cocoa

Si vous avez une question concernant Xcode ou Interface Builder, commencez par consulter la documentation de rfrence des outils du dveloppeur. Avec Mark Dalrymple, nous avons crit un ouvrage sur les aspects internes de Mac OS X du point de vue du dveloppeur. Si votre code doit travailler avec le systme dexploitation, par exemple pour du multithread ou des oprations rseau, je vous conseille fortement dobtenir un exemplaire du livre Advanced Mac OS X Programming. Nhsitez pas exprimenter. La plupart des questions trouveront une rponse en crant une petite application. En gnral, cela ne prendra pas plus de 15 minutes. Le site web ddi cet ouvrage (www.bignerdranch.com/products/cocoa1.shtml) contient les rponses de nombreuses questions et plusieurs exemples amusants. Le Wiki CocoaDev (www.cocoadev.com/) propose de nombreuses astuces. Apple gre une liste de diffusion pour les dveloppeurs Cocoa. Vous pouvez vous y abonner en allant sur le serveur des listes de diffusion dApple (http://lists.apple.com/). Les archives de cette liste se trouvent sur le site www.cocoabuilder.com/. De nombreux blogs concernant Cocoa, y compris le podcast "Late Night Cocoa", sont syndiqus ladresse www.macdevnet.com/. Si vous avez puis toutes les autres possibilits, lassistance technique du dveloppeur dApple rpondra vos questions moyennant une contribution nancire. Ces personnes ont de grandes connaissances et se rvlent trs utiles. Elles ont rpondu au grand nombre de questions que je leur avais poses. Rejoignez lApple Developer Connection. Vous aurez ainsi accs aux versions les plus rcentes des outils du dveloppeur et de la documentation. Ladresse du site web ADC est http://connect.apple.com/. Plusieurs sites web en franais proposent de vous initier Cocoa, dapprofondir vos connaissances ou de rejoindre la communaut des dveloppeurs. Allez par exemple sur www.cocoa.fr ou www.osx-dev.com.

m m

Enn, soyez sympa. Aidez les dbutants. Proposez des applications utiles et leur code source. Rpondez gentiment aux questions. La communaut est relativement petite et les bonnes actions sont rcompenses. Merci davoir lu mon livre !

Exercice
crivez une application Cocoa qui sera utilise par une autre personne que vous.

Index
Symboles #de ne 202 #import 20, 25, 186, 202 #include 26 #pragma mark 137, 248, 253, 267, 303, 341, 350 $ (dans des indicateurs) 228 %@ 40, 44, 73, 228 + (dans les noms de mthodes) 54 +, pr xe 203 .????, extension 163 .doc, chiers 276 .framework, extension 6 .h, chiers 28, 149 .m, chiers 28 .rsmn, extension 163 .tar, chiers 414 .tgz, chiers 414 : (dans les noms de mthodes) 37 @, symbole 26, 41 @avg 122 @class 26, 185 @count 122 @end 26, 30 @implementation 26, 30 @interface 20, 26 @max 122 @min 122 @optional 167, 296 @property 26, 120 @protocol 26 A Abstraites, classes 126, 155 acceptsFirstResponder 261, 268 Accesseurs, mthodes 50, 76, 115, 119, 121, 143 Actions 24, 82 mthodes 20 sur nil chier nib 288 nextResponder 287 presse-papiers 286 recherche dans la chane des rpondeurs 287 Actions Voir aussi Cible/action Active Build Con guration 42 Actives, fentres 259 add 127, 132, 178, 368 addEmployeesObject 367 addObject 37, 46, 65 addObjectsFromArray 46 addObserver 123, 146, 210, 412 addObserver:selector:name:object: 211 Adobe PDF 2, 258, 279, 291, 337 Advanced Mac OS X Programming 416 Af ne, transf orme 235, 377 allKeys 199, 331 alloc 36, 74 Annonceur 210, 214 Annuler 168 Annuler/rtablir 140, 151 @selector 26, 92 @sum 122 @synthesize 26, 120

418

Index

AppController 87, 185, 249, 410 appendData 413 AppKit, framework 7, 81, 108, 279 Apple, Inc. Copland 3 Developer Connection 416 Developer Technical Support 416 et NeXT 4 guides pour linterface graphique 18 histoire 1 liste de diffusion des dveloppeurs Cocoa 416 Mac OS X 3 applicationShouldOpenUntitledFile 205 archivedDataWithRootObject 162 Archiver/dsarchiver boucles innies 166 dcoder 156 encoder 155 extension et icne 163 Info.plist 158, 163, 168 initWithCoder 154 NSCoder 154, 166 NSCoding 154, 167 NSDocument 158, 168 NSDocumentController 158 NSKeyedArchiver 155, 162, 166 NSKeyedUnarchiver 162, 166 NSWindowController 161, 163 objets 17 updateChangeCount: 168 UTI (universal type identier) 168 Arguments et mthodes 37, 58, 202 aSelector 92, 109, 211 Assertion, vri er 63 assign 121 Assistants, objets connecter 105 dataSource 101 et dlgus 98, 108 Atomiques, mthodes 121 Atteignables, objets 373

attributedStringForObjectValue 336 Attributs 172 assign et retain 121 chanes avec 274, 336 dictionnaire 277 inspecteur 19, 174 proprits 120 Automatique compltion 335 objets libration 54, 74 autoscroll 257, 314 availableTypeFromArray 285 awakeFromNib 30, 350 B becomeFirstResponder 261, 268 beginSheetForDirectory: le:types:modalForWindow:modalDelegate:didEndSelector:contextInfo: 252, 279 beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo: 219 Berners-Lee, Tim 3 Big Nerd Ranch, Inc. 4, 7, 416 BigLetterView 262, 271, 277 ajouter le gras et litalique 282 application dentranement la saisie 308 cut:, copy: et paste: 285 glisser-dposer 298 bind:toObject:withKeyPath:options: 122 Bindings, inspecteur 116, 131, 174 blastem 388, 391 BMP 258 BNR, pr xe 203, 295 Bote liaisons 180 taille 360 Bote bleue oue 271 BOOL 25 boolForKey 201

Index

419

Boucle dexcution 314, 409 de gestion des vnements 32 bounds 236, 267, 279 Boutons 17, 82, 100, 103, 118 attributs 84, 133, 310, 411 liaisons 133 BSD Unix 2 Bundle 13, 195 C C langage de programmation 5 tableaux 376, 378 types primitifs 374 CAAnimation 389 CALayer 385, 389 calendarDate 54 Calendrier, indicateurs dans la chane de format 56 CAOpenGLLayer 393 Caractres sur plusieurs octets 41 CarArrayController 182 CarLot, projet 172 Case cocher 192 caseInsensitiveCompare 134 Catgories 293 CATextLayer 393 Cellules 240 CFMakeCollectable() 375, 377 CGColorRef 376, 381 Chane des rpondeurs 287, 371 Chanes 47, 274 partielles, valider 334 vides 91, 299, 331, 335 Champ de texte 18, 86 attributs 346 inspecteur 86 liaisons 118, 329 change 123, 146, 148

changeBackgroundColor 190, 204, 213 changeKeyPath 148 changeNewEmptyDoc 189, 192, 205 changeParameter 396, 398, 400 changeViewController 355, 359 char 25 characters 261 Chargement dun cadre, dlgu 351 Charger des chiers 162 Chemins de cls 122 Cible 82 xer par programmation 92 Cible/action 24 classe AppController 91 dboguer 93 entres de menu 92 chier nib 88 sous-classes de NSControl 84 SpeakLine, projet 87 Classes 5, 6 crer 50 existantes 37 messages, envoyer 36 mthodes de 54 Objective-C, crer 48 rfrence en ligne 7 rutilisables 133 Clavier, vnements BigLetterView 262, 271 bote bleue oue 271 boucle des vues actives 263 effets de survol 270 rstResponder 259, 267 interpretKeyEvents: 268 NSEvent 261 NSResponder 261 TypingTutor, projet 262 vue personnalise 262 Cls noms 202 observables 118 Clich 290

420

Index

clickCount 247, 248 Cocoa/OpenGL, application crire lapplication 396 utiliser NSOpenGLView 395 Code de retour 316, 323 en mode dual 69, 374 Coller, implmenter 283 Colonne de tableau attributs 135, 347 liaisons 132, 179 ColorFormatter chercher des sous-chanes dans une chane 331 ColorFormatter.h 327 chier nib 328 mthodes 331 NSColorList 330 compare 134 Compilateur 4, 35, 60 Compilation 28 Composer une image 253 Composition compare lhritage 48 compteur 27 Compteurs de rfrences 71 analogie du chien et de la laisse 71 dsallocation 73 garder et relcher 71 mthodes accesseurs 76 NSAutoreleasePool 74 objets libration automatique 74 concludeDragOperation 302 Conditionally Sets Editable 179 Connexions 22, 89 Console (journal) 28 containsObject 46 control 333 control:didFailToFormatString :errorDescription: 333 Contrleur classes 125, 132

de tableau 122, 130, 149 attributs 130, 175, 368 liaisons 131, 174 convertPoint:fromView: 255 Coordonnes, systmes 242, 254 Copie paresseuse 289 Copier, implmenter 283 Copier-coller, oprations 283 copy 121, 285 Core Data formateur numrique, attributs 175 framework 7 graphe dobjets 181 indicateur de niveau 176 interface 174 liaisons 177 NSArrayController 171, 178, 182 NSManagedObjectContext 171, 181 NSManagedObjectModel 171 NSPersistentDocument 172, 181, 355 objets 181 rcuprer des donnes 174, 181, 368 relations agencement de linterface 367 classes NSManagedObject personnalises 365 DepartmentView.nib 367 EmployeeView.nib 369 vnements et nextResponder 371 inverses 364 liaisons 369 modle de donnes 364 Views & Cells 174 Core Foundation (CF), structures de donnes 375 Core, animation CALayer 385 et CAAnimation 389 graphe dobjets 386 polynmes 386 CoreGraphics, framework 2 Couleur darrire-plan 206, 213, 266 count 45, 199

Index

421

countOfSongs 143 Coup dil 168 Couper, implmenter 283 Couples cl-valeur 123, 168, 198, 224 Courbe de Bzier 235, 243 Cox, Brad 35 createEmployee 137, 150 createNewPolynomial 378, 388, 389 currentEvent 314 currentRect 256 Curseurs 85, 116, 250, 397 attributs 85, 116, 250, 319, 398 de cellules 398 continus 85, 116, 250, 397 inspecteur 85 liaisons 117, 250, 320 cut 285 D Dalrymple, Mark 416 Darwin 2 dataForType 285 dataOfType 159, 162, 409 dataReady 413 dataSource 101, 137, 406 dataSource (NSTableView) 102 dataWithPDFInsideRect 279 Date, formateur 86, 325 dateByAddingYears 55 dateWithYear 54 dayOfCommonEra 55 dayOfMonth 55 dayOfWeek 55 dealloc 73, 83 Dbogueur 4, 44, 53, 60, 93 Dclarer un type 168 declareTypes 284, 290 decodeBoolForKey 156 decodeDoubleForKey 156

decodeFloatForKey 156 decodeIntForKey 156 decodeObjectForKey 156 Dcoder 156 defaultCenter 211 defaults (Terminal) 207 delegate 99, 107, 205 Dlgu 98, 108, 215 mthodes 296 deleteRandomPolynomial 378, 388, 390 deltaX 247 deltaY 247 deltaZ 247 Department, entit 364 DepartmentView.nib 367 Dsallocation 71 Dsarchiver, objets 17 Descripteurs de tri 135 description, mthode 44, 52, 62, 86 Dessiner avec NSBezierPath 236 avec OpenGL 395 texte avec des attributs BigLetterView 277 gnrer du PDF 279 gras et italique 282 NSAttributedString 274 NSFont 274 NSFontManager 281 NSMakeRange() 274 NSMutableAttributedString 275 NSShadow 281 NSString 277 Dictionnaire attributs 277, 336 change: 123, 146, 148 couples cl-valeur 168, 199 NSDictionary 198, 408 NSMutableDictionary 198 userInfo 214, 323 UTI (universal type identier) 168

422

Index

displayViewController 358, 371 Division, vues 230 Documents application base sur 127 architecture de 157 contrleur de 158 extension 163 icne 163 objet 127 persistants 172, 181, 355 sans titre, supprimer la cration 205 types 165 draggingEntered 301 draggingExited 301 draggingSourceOperationMaskForLocal 298, 301 draggingUpdated 301 dragImage:at:offset:event:pasteboard:sour ce:slideBack: 299 drawAtPoint 277 drawInRect 277 drawInRect:fromRect:operation:fraction: 254 drawInRect:withAttributes: 277 drawRect 234, 254, 256, 267, 279, 302, 338, 388, 396, 401 E diteur, af chage 15 Effets de survol 270 Embarquer des objets dans une bote 177, 310 dans une matrice 241, 397 dans une vue dlement 238 Employee, entit 364 EmployeeView.nib 369 encodeBool:forKey: 155 encodeConditionalObject:forKey 166 encodeDouble:forKey 155 encodeFloat:forKey 155

encodeInt:forKey 155 encodeObject:forKey 155 Encoder 155 conditionnel 166 encodeWithCoder 154 encodeWithCoder:forKey 154, 162, 166 endSpeedSheet:returnCode: 322 Enregistrer ajouter une application 161 panneau 279 En-tte, chiers 19, 26 Entit 172, 181, 364 entryDate 50, 63, 73 numrations 199 Erreurs 162 ET (&) 246 vnements boucle de gestion 32 et nextResponder 371 le dattente 19, 32 mthodes de traitement 245, 255 objet 246 souris 247 Exceptions 60, 93 exec() 403 Extensions 6, 28, 41, 163 F Faibles, rfrences 374 Fentres actives 152, 259, 287 attributs 319, 361 redimensionner 110, 193, 319, 360 serveur 2, 32 Feu, bouton 387 Feuilles 252 ajouter 316 contextInfo 322 fentres modales 323 graphe dobjets 317

Index

423

modalDelegate 316 NSApplication, mthodes 315 outlets et actions 317 panneau dalerte 218 Fichiers connexes 28 den-tte 26 dinterface 26 encoder et localiser 225 enveloppe 159 nib 17 plusieurs 183 Files Owner 19, 190 leWrapperOfT ype 159 llRect 235 nalize, message 373 FirstLetter, catgorie 293 rstResponder 259, 267, 287 agsChanged 261 oat 134 oatF orKey 201 oatV alue 85 Focus (d)verrouiller 234, 299 cycle 271 fontWithName 274 for, boucle 39, 52 fork() 403 Formateurs 86, 131, 174, 325 Fortes, rfrences 374 forwardInvocation 140 Foundation Tool 37 Foundation, framework 6, 129 Frameworks 3, 6, 386 AppKit 7 Core Data 7 Foundation 6 Free Software Foundation 35, 60, 64 Fuite de mmoire 72, 74, 378, 381, 404

G Garder puis librer, idiome 77 gcc (GNU C compiler) 4, 35, 60 gdb (GNU debugger) 4, 60 console 62 generalPasteboard 284, 286 Gnrateur de nombres alatoires, application classe, crer 19 compiler et excuter 28 dpanner 29 chier dimplmentation 27 fonction main 15 instance, crer 21 interface utilisateur, agencer 17 objets 22 projet, crer nouveau 12 genstrings 227 Gestion des versions, systmes 189 Gestionnaire dannulation 151 get mthodes 78, 115, 120 prxe 78 getObjectValue:forString:errorDescription: 326, 332 GIF 258 glEndList() 402 Gliss 396 Glisser-dposer destination 301 mthodes de 303 draggingSourceOperationMaskForLocal: 298, 305 draggingUpdated 305 masque dopration 305 mise en exergue 302 presse-papiers 283 registerForDraggedTypes 301 retour pour lutilisateur 297 source 298 glLoadIdentity() 401 GLUT.framework 396

424

Index

Graphe dobjets 68, 181 Gras et italique 282 Guillemets 41, 203, 224 H handleColorChange 214 Hritage 25, 43, 48, 83, 261 diagramme 83 et composition 48 graphe 43 hidesOnDeactivate 184 Hirarchie de vues 230 hourOfDay 55 HTML 276 HTTP 343 I IBAction 25, 28 IBM 81 IBOutlet 25 ibtool 227 Icnes 163 panneau dalerte 217 id 25 Identi ant A WS 347 Identity, inspecteur 21, 88, 182 Idiomes garder puis librer 77 librer automatiquement lancienne valeur 78 vrier avant de modier 78 Image 253 formats de chiers 258 opacit 248, 253 reprsentations 257 Image Well 176 ImageFun 231, 314 Immuabilit 45, 54 Implmentation, chier 19, 27

Imprimer isDrawingToScreen 342 NSPrintOperation 337 pagination 338 incrementFido 118 Indentation dans Xcode 26 fonction de la syntaxe 26 indexOfObject 46 Indicateur de progression attributs 310 liaisons 311 Indicateurs 40, 47, 56, 228 In nies, boucles 166 Info.plist 158, 163, 168 init 36, 44, 57, 58, 83 init, red nir 59 initialFirstResponder 265 Initialiser, bouton 23 Initialiseurs 57 avec arguments 58 dsigns 59 initWithCoder 154, 401 initWithData:encoding: 408, 413 initWithEntryDate 59, 63 initWithFormat 47 initWithFrame 237, 253, 266, 278, 340 initWithFrame:pixelFormat: 395 initWithPeople 340 inLetterView 312 insertObject:atIndex: 46 Inspecteur 19, 23, 130 Instances 6, 36 variables 5, 20 Instruments 381 integerForKey 201 Interface Builder 4, 8, 16, 30, 89 Interface utilisateur, agencer 17, 190 Interface, chier 26 interpretKeyEvents 268

Index

425

Invocations 140 iPing 410 isa, pointeur 64 isARepeat 261 isDrawingToScreen 342 isEqual 44 isFlipped 242 isOpaque 268 isPartialStringValid:newEditingString: errorDescription: 334 Italique, texte 282 Itrateurs 199 J Java code 25 interface 154 Jobs, Steve 1 JPG 258 Justi cation du texte 18 K keyCode 262 keyDown 83, 261, 268, 371 keyEnumerator 199 keyUp 261 keyWindow 184 knowsPageRange 338, 341 KVC (key-value coding) Add Localization (Xcode) 222 chemins de cls 122 et nil 133 exemple de projet 114 chier nib 222 genstrings 227 ibtool 227 langues 222 liaisons 116 Localizable.strings 225 NSBundle 222, 225

NSLocalizedString 227 observateurs 117 ordre explicites des indicateurs 228 proprits et attributs 120 relations non ordonnes 144 relations ordonnes 143 L lastObject 45 Late Night Cocoa, podcast 416 laterDate 57 length 47, 93, 136, 274, 330, 334 Liaisons 116, 122, 130, 136 cellules, vues dlement ou vues tableau 130, 178 Core Data 177 DepartmentView.nib 367 EmployeeView.nib 369 NSArrayController 136 NSUserDefaultsController 207 Librer automatiquement lancienne valeur, idiome 78 Localizable.strings 224, 225 location 274, 294, 330, 335, 341 locationInWindow 246, 255 lockFocus 299 lottery.m 51 LotteryEntry, classe 49 M Mac OS X aspect du serveur de fentres 2 Outils du dveloppeur 4 synthtiseur vocal 87 main() 15, 42, 61 MainMenu.nib 16, 88, 186, 206, 339, 409 mainWindow 184 malloc() 373 Managed Object Class 365

426

Index

managedObjectContext 174, 181, 355, 358, 365, 369 ManagingViewController 355, 357 Matrice attributs 242, 398 taille 399 Mmoire, gestion compteurs de rfrences 71 dealloc 73 fuite de mmoire 72, 74, 378, 381, 404 mthodes accesseurs 76 objets libration automatique 74 objets temporaires 75 ramasse-miettes 69 messageFontOfSize 274 Messages 5, 6, 36, 42, 64 retransmission 140 Mthodes 6 accesseurs 50, 76, 115, 119, 121, 143 action 20 arguments 37 ColorFormatter 331 de classe 54 dnir 6 tapes du nommage 215 formateur 326, 331 gestion des vnements 245 get, prxe 78 NSString 93 NSTextField 86 prives 295 publiques 27 set 77, 120 atomiques 121 traiter les vnements 255 minuteOfHour 56 Mise en exergue, glisser-dposer 302 Mise en majuscules 8, 13, 21, 94, 134 modalDelegate 316 Modales fentres windows 323 oprations 218, 253 Modles, classes 125

modi erFlags 246, 262 Modularit et panneaux 184, 353 monthOfYear 56 Mots de passe, af cher des puces 86 mouseDown 83, 245, 247, 254, 299, 314 mouseDragged 246, 247, 255, 257, 314 mouseEntered 270 mouseExited 270 mouseMoved 270 mouseUp 246, 247, 248, 254, 314 mutableArrayValueForKey 143 mutableCopy 46 MyDocument 127, 136, 144, 149, 161 MyDocument.h 129, 136, 145, 149, 219, 341, 355, 406 MyDocument.m 129, 137, 144, 339, 358, 371, 406 MyDocument.nib 129, 134, 136, 174, 227, 354, 360, 406 N name 210 NeXT Computer, Inc. 1 NeXT Software, Inc. 1 nextKeyView 263, 304 nextResponder 286, 287, 371 NeXTSTEP 2 Nib (NeXT Interface Builder), chiers 15, 17, 19, 30, 89, 183 nil 25, 42, 47, 133 comme cible 286 envoyer un message 43 NO 25 Nombres, formateur 131 attributs 132, 175 Noms doutil 38 nonatomic 121 Non-objet, types de donnes 374

Index

427

Noti cations annonceur 210, 214 consigner larrive 214 dlgus 215 tapes de nommage dune mthode 215 NSNotication 210 NSNoticationCenter 210 observateur 209 poster 213 prxes 213 sinscrire pour recevoir 211 userInfo, dictionnaire 214 NSAf neT ransformStruct 235 NSAlert 108 NSAllocateCollectable() 374, 377, 383 NSAnimation 108 NSAnimationContext 385, 391 NSApplication 19, 108, 288, 289, 315 NSApplicationMain() 15 NSArray 45, 406 NSArrayController 163, 178, 182, 371 KVC et nil 133 liaisons 136 modle-vue-contrleur (MVC) 126 trier 134 NSAssert() 63 NSAttributedString 274 NSAutoReleasePool 39, 74 NSBezierPath 235, 243, 258 NSBox 230 NSBrowser 108 NSBundle 66, 195, 225 NSButton 82, 84, 120, 241 NSButtonCell 241 NSCalendarDate 54 comparer 57 diffrence entre 57 NSCAssert() 64 NSCell 240 NSClipView 238 NSCoder 154, 166, 401

NSCoding 154, 167 NSColorList 330 NSColorWell 82 NSControl 82, 83, 333 NSControl, sous-classes 84 NSController 126 NSData 142, 159, 161, 348 NSDate 54, 57 NSDateFormatter 87, 325, 326 NSDatePicker 108, 176 NSDecimal 235 NSDictionary 198, 408 NSDocument 158, 168, 172 NSDocumentController 288 NSDraggingInfo, protocole 303 NSDrawer 108 NSEvent 246, 261 NSException 60 NSFont 274 NSFontManager 108, 281 NSFormatter 86 chanes avec attributs 336 chanes partielles, valider 334 ColorFormatter 327 compltion automatique 335 mthodes de formatage 331 NSControl, dlgu 333 rechercher des sous-chanes dans des chanes 331 NSGarbageCollector 70 NSGradient 302 NSImage 108, 249, 253, 257 composer 253 dessiner sur 299 NSImageView 176 NSInputManager 281 NSInvocation 139 NSKeyedArchiver 155, 162, 166, 203, 213 NSKeyedUnarchiver 155, 162, 166, 204, 207 NSLayoutManager 108

428

Index

NSLevelIndicator 176 NSLocalizedString 227 NSLog() 40 NSMakeRange() 274 NSManagedObject, classes 365 NSManagedObjectContext 171, 181, 353, 355 NSMatrix 109, 240, 241 NSMenu 109 NSMutableArray 36, 43, 46, 70, 135 NSMutableAttributedString 275 NSMutableDictionary 198 NSNoti cation 210 NSNoti cationCenter 210 NSNull 47 NSNumber 37 NSNumberFormatter 326 NSObject 44, 64, 83, 109, 123 NSObjectController 126 NSOpenGLView 395 NSOpenPanel 208, 248 NSPanel 184, 191 NSPasteboard 284 NSPathControl 109 NSPersistentDocument 172, 181, 355 NSPipe 403, 406, 410, 412 NSPoint 235, 246, 255, 277, 299 convertir entre des vues 255 NSPrintOperation 337 NSProgressIndicator 308 NSRange 235, 274, 330, 338 NSRect 235, 240 NSResponder 83, 245, 261, 371 NSRuleEditor 109 NSRunAlertPanel() 217 NSSavePanel 109, 279 NSScannedOption 375, 383 NSScrollView 230, 238 NSSecureTextField 86

NSShadow 281 NSSize 235 NSSlider 82, 85, 120, 125, 250, 397 NSSliderCell 241 NSSortDescriptor 135 NSSound 109 NSSpeechRecognizer 109 NSSpeechSynthesizer 91, 98, 103, 109 NSSplitView 109, 230 NSString 41, 43, 224, 277 mthodes 47, 93 ajouter 293 NSTableView 101, 109, 406 NSTabView 109, 230 NSTask .tar et .tgz, chiers 414 graphe dobjets 407 lectures asynchrones 409 multithread contre multitraitement 404 ping 410 zipinfo 404, 414 NSText 109 NSTextField 84, 85, 93, 109, 120 NSTextFieldCell 241 NSTextStorage 109 NSTextView 82, 109, 151 NSTimer AppController 312 apprentissage de la saisie, projet 308 autodlement bas sur une minuterie 314 graphe dobjets 308 NSProgressIndicator 308 NSRunLoop 314 NSTokenField 109 NSToolbar 109 NSUndoManager ajouter lannulation RaiseMan 142 annuler des modications 146 et les fentres 151 KVC (key-value coding) 143 KVO (key-value observing) 146

Index

429

modier sur insertion 149 NSInvocation 139 piles des annulations et des rtablissements 140 NSURL 343 NSURLConnection 343, 351 NSURLRequest 343 NSUserDefaults 197, 200 inscription 200 lecture 200 modication 200 NSView 83, 229, 230, 237, 240, 279, 299, 356 crer une nouvelle instance 232 systme de coordonnes 254 NSViewController 353, 355, 371 NSWindow 17, 83, 109, 287 outlet initialFirstResponder 90 NSWindowController 161, 163, 183 NSXMLDocument 344, 348 NSXMLNode 344, 347, 349 NULL 25 numberOfRowsInTableView 102, 106, 137, 350, 408 O object, mthode 210 ObjectAllocations, instrument 381 objectAtIndex 45 objectEnumerator 200 objectForKey 199, 201 Objective-C 3, 35 chanes de format 40 classes crer 48 existantes 37 code 25 conventions typographiques 8 dbogueur 60 description, mthode 52 extensions 28, 41 fonctions, appeler 28

initialiseurs 57 instances 36 messages 64 mots cls 26 NSArray 45 NSMutableArray 43, 46 NSObject 44 NSString 43, 47 protocole 154 sous-classes 48 spcicateurs de visibilit 27 types et constantes 25 Objects & Controllers 21, 130 objectValue 86 Objets libration automatique 54, 74 archivs 17 atteignables 373 chemins de cls 122 compteurs de rfrences 71 connecter 22 copies 40 Core Data 181 dnis 5 dlgus 98, 108 dsarchivs 17 graphe 68, 181 invisibles 19 mthodes accesseurs 76 po 44, 62 pointeurs 65 temporaires 75 Objets Voir aussi Archiver/dsarchiver Observateurs 117, 209 observeValueForKeyPath:ofObject:change :context: 123, 146, 148 Obtenir (objets grs) 174, 181, 368 Opacit 268 opacity 253 OpenGL 385, 395 application 395 OpenGL.framework 396 vue OpenGL, attributs 397

430

Index

openItem 350 OpenOf ce 276 openPanelDidEnd:returnCode:contextInfo: 249, 252 OpenStep 3 Oprateurs 122 Opration, masque 305 otherMouseDown 245 otherMouseDragged 246 otherMouseUp 246 Outils de base 37 de dveloppement 4 en ligne de commande 207, 404 Outlets (variables dinstance) 20 Ouvrir les chiers connexes dans le mme diteur 28 P Panel, attributs 193 Panneaux 184 dalerte conrmer une suppression 218 feuille 218 icne 217 NSRunAlertPanel() 217 opration modale 218 removeEmployee: 219 Paramtres par dfaut 200 paste 285 pasteboardChangedOwner 290 pasteboardWithName 284 PDF (portable document format) 2, 258, 279, 291, 337 PeopleView 339 performClick 346 performDragOperation 302 Persistance, framework 7 Person, classe 127, 136 Person.h, chier 128

PICT 258 Pixels 243 Plage de caractres 275 de nombres 274 Plug-in 13 PNG 258 po (print-object) 44, 62 Pointeurs 23, 36 Points darrt 60 symboliques 62 mesure 243 Polices de caractres, mthodes 274, 281 Polynmes 386 Polynomial, vue effets 387 identit 379 Pool de libration automatique 74 Poster une noti cation 213 postNoti cation 212 postNoti cationName 212 PostScript 2 Prfrences, panneau congurer le menu 186, 188 graphe dobjets 185 NSBundle 195 NSPanel 184 NSWindowController 183 PreferenceController.m 186, 189, 193 Preferences.nib 189 Rinitialiser les prfrences, bouton 208 prepareRandomNumbers 50, 57 prepareWithInvocationTarget 141, 145, 147 Presse-papiers chane des rpondeurs 287 copie paresseuse 289 cut:, copy: et paste: (BigLetterView) 285 dclarer les types 283 NSApplication 287, 289 NSPasteboard 284

Index

431

pasteboardChangedOwner: 290 serveur 283, 290 texte PDF 291 pressure 247 printDocument 339 printf 40 print-object (po) 44, 62 Priorit des valeurs par dfaut 202 Prives, mthodes 295 Project Builder 12 Projet, rpertoire 12 Proprits attributs 120 listes de, classe 204 NSManagedObjectModel 171, 355, 365 Protocoles 154, 167, 296 informels 296 Publiques, mthodes 27 Python, langage de programmation 5 Q Quartz 242, 395 QuartzCore, framework 376, 386 Quitter, entre de menu 33 R RaiseMan, application .rsmn, extension 163 et annulation 140 Interface Builder 129 NSArrayController 127 Xcode 127 Ramasse-miettes code en mode dual 69, 374 nalize, message 373 gestion de la mmoire 69 Instruments 381 malloc() 373 NSAllocateCollectable() 374, 377, 383 objets atteignables 373

polynmes 375 rfrences faibles 374, 383 fortes 374 types de donnes non-objet 374 versions de Mac OS 68, 71 RANDFLOAT() 376 random() 28, 50, 57, 63, 73, 237, 390 rangeOfString:options: 330 readFromData:error: 160, 162 readFromFileWrapper:error: 160 readFromPasteboard 285, 294, 304 readFromURL:error: 160, 406 readonly 121 readwrite 121 rectForPage 338, 341 registerDefaults 201 registerForDraggedTypes 301 Rinitialiser les prfrences, bouton 208 Relations 122, 142, 172 142, 143, 363 release 36, 71, 83 reloadData 102 remove 127, 132, 178, 220, 368 removeAllObjects 46 removeEmployee 219 removeEmployeesObject 367 removeObject 46 removeObjectAtIndex 47 removeObjectForKey 200, 201 removeObserver 212 reshape 396 resignFirstResponder 261, 268, 271 resizeAndRedrawPolynomialLayers 392 respondsToSelector 110 retain 71, 83, 121 Retourner une vue 242 rightMouseDown 245 rightMouseDragged 246

432

Index

rightMouseUp 246 RTF 276 RTFD 276 Ruby, langage de programmation 5 runModalForWindow 323 S savePDF 280 sayIt 88, 90, 99, 103, 107 Scanned 375 scrollWheel 246 Sculley, John 1 secondNumber 50, 57, 73 seed 20, 27 Slecteur 37, 92, 139, 307, 316 tableau 65 Selector 134 Serveur de fentres 32 Services web Amazon 344 AmaZone 344 code 347 fetchBooks: 348 identiant AWS 347 interface 345 NSURL 343 NSXMLDocument 344, 348 NSXMLNode 344, 347, 349 webView: 351 XML 347 set, mthodes 77, 120 setBool:forKey: 201 setCalendarFormat 56 setData:forType: 285 setEmployees 129 setEnabled 84 setEntryDate 50, 58, 79 setExpectedRaise 134 setFido 115, 121 setFloat:forKey: 201

setFloatValue 85 setFrameLoadDelegate 351 setImage 256 setInteger:forKey: 201 setMainFrameURL 351 setNeedsDisplay 234, 243, 253, 278 setNilValueForKey 134 setObject:forKey: 200, 201 setObjectValue 86, 102, 138, 326 setState 84 setString 278 setString:forType: 285 setStringValue 86, 93 setValue:forKey: 114, 134 setValue:forKeyPath: 122 showOpenPanel 249, 251 showSpeedSheet 318 showWindow 188, 194 sigint, signal 409 Sites Web de ce livre 416 dveloppement de Darwin 2 Free Software Foundation 64 The Objective-C Language PDF 5 size 277 Size, inspecteur 110, 233, 240 sizeWithAttributes 277 Smalltalk, langage de programmation 35 sortDescriptorsDidChange 136 sortUsingDescriptors 135 Souris, vnements 247 #pragma mark 248 autodlement 257 clickCount 248 composer 253 effets de survol 270 et opacit dimage 248, 253 NSEvent 246 NSImage 249, 253, 257 NSOpenPanel 248

Index

433

NSResponder 245, 371 recevoir 247 systme de coordonnes dune vue 254 Sous-chanes, rechercher dans des chanes 330 Sous-classes 20, 26, 48, 81 speechSynthesizer:didFinishSpeaking: 98 speechSynthesizer:willSpeakPhoneme: 98 speechSynthesizer:willSpeakWord: 98 speedSheet 317, 320 srandom() 28 standardUserDefaults 201 startButton 100, 103, 107, 410 startStopPing 411 state 84 stopButton 100, 103, 107 stopGo 311, 313, 317 stopModalWithCode 323 StretchView 232, 237, 239, 249, 314 stringByAppendingString 47 stringForObjectValue 326, 332, 336 stringForType 285 stringValue 86, 93 stringWithFormat 75, 93 Structures 6, 110, 235 Subversion 189 subviews 230 Super-classe 25 superview 230 Supprimer, bouton 133, 220 Synthtiseur vocal 87, 98 Systme de chiers, v aleurs par dfaut 200 T Tableaux 40 modiables 46 tableView 103, 137, 149, 345, 406, 408 tableView:objectValueForTableColumn:row: 102, 107

Taille, informations 233 Taligent 81 target 23, 82 taskTerminated 413 Tmoin de couleur 185, 190, 205 liaisons 328 Temporaires, objets 75 Terminal, outil 165, 207 terminate 33 Terminologie de la programmation oriente objet 5 Texte 274 ombr, style 281 textField, outlet 23, 90 The Objective-C Language 5 TIFF 258 timeIntervalSinceDate 57 timestamp 247 titleBarFontOfSize 274 toolTipsFontOfSize 274 Touches de modi cation 246, 262, 305 Trier 134 Type dclarer 168 Objective-C 25 TypingTutor, projet 262, 307 U unarchiveObjectWithData 162 unbind 122 Undo/redo 140 undoManager 141, 152 undoManagerForTextView 151 Unicode 41, 47 Universal type identi er (UTI) 168, 405 Universit de Californie, Berkeley 2 Unix 2, 403

434

Index

updateChangeCount 168 userFixedPitchFontOfSize 274 userFontOfSize 274 userInfo, dictionnaire 214, 323 UTExportedTypeDeclarations 168 V Valeurs par dfaut de lutilisateur couleur darrire-plan 206 documents sans titre 198, 205 inscrire 203 modies 204 noms des cls 202 NSDictionary 198 NSMutableDictionary 198 NSUserDefaults 200 NSUserDefaultsController 207 outil en ligne de commande 207 prxe des variables globales 203 valueForKey 114 valueForKeyPath 122 Variables dinstance 5 globales 202, 207, 213, 218, 276, 284 Vri er a vant de modi er , idiome 78 Verrous 121 Views & Cells 17, 131 Visibilit, spci cateurs 27 void 25, 28 Vue image, liaisons 179 Vues actives 263 calque 393 cellules 240 classe 125 crer depuis le code 240 dlement 230, 238 division 230 drawRect: 234 changer changeViewController: 355, 359

conception 354 displayViewController: 358, 371 graphe dobjets 354 ManagingViewController 355, 357 NSViewController 353, 355 redimensionner la fentre 360 hirarchie 230 ImageFun, projet 231 inspecteur Size 233 isFlipped 242 NSBezierPath 235, 243 NSScrollView 238 NSView 230, 237, 240 retourner 242 sous-classe 232 suprieures 233, 240, 287 systme de coordonnes 254 tableau attributs 105, 219 dlgu 107 source de donnes 101, 408 texte, attributs 151 W WebKit, framework 351 Wiki CocoaDev 416 window 230, 247 windowControllerDidLoadNib 161, 206 windowDidLoad 194, 204 windowWillReturnUndoManager 152 Wozniak, Steve 1 writeToPasteboard 285, 300 writeToURL 159 X x et y, coordonnes 242, 254 X window, serveur 2 Xcode 4 #pragma mark 248 application base sur des documents 127, 172

Index

435

cibles 69 console 28 contrleur, nouvelle classe 88 documentation 31 et ramasse-miettes 380 et valeurs par dfaut 207 Foundation Tool 37 icnes et extensions 163 Info.plist 158, 163, 168 localisation 222 points darrt 60 prfrences 26 RandomApp (gnrateur de nombres alatoires) 12 rpertoire de projet 12 XIB, chiers 189

XML 168, 189, 343, 347 donnes 348 nuds 347 XPath 347 Y YES 25 Z Zip, chiers 405 ZIPspector 404 Zombies 94 Zone de suivi pour les survols 270

Rfrence

Programmation COCOA sous MAC OS X 3e dition


Que vous dveloppiez dj des applications pour Mac OS X ou que vous dbutiez, Programmation Cocoa sous Mac OS X est louvrage quil vous faut. Considr comme LA rfrence en matire de programmation Mac, ce manuel, conu sous la forme dun tutoriel, vous guidera tout au long des tapes qui vous permettront de comprendre la programmation Cocoa. Avec de nombreux exercices, dont la clart et lexactitude ont t prouves lors de sessions de formation, cet ouvrage traite lessentiel du dveloppement dapplications pour Mac OS X et constitue une ressource indispensable tout programmeur Mac. Vous vous initierez au langage Objective-C et apprendrez utiliser les trois outils les plus employs par les dveloppeurs Mac : Xcode, Interface Builder et Instruments. Vous y dcouvrirez galement les principaux motifs de conception de Cocoa. Cette troisime dition se fonde sur les technologies apportes par Mac OS X 10.4 et 10.5. Elle couvre notamment Xcode 3, Objective-C 2, Core Data, le ramasse-miettes et CoreAnimation. Si vous voulez obtenir de laide ou vrier la solution des exercices, rendez-vous sur le site web www.bignerdranch.com/products/.
TABLE DES MATIRES

Prsentation de Cocoa Premiers pas Objective-C Gestion de la mmoire Cible et action Objets assistants Modles de conception KVC et KVO NSArrayController NSUndoManager Archivage Bases de Core Data Fichiers nib et NSWindowController Valeurs par dfaut de lutilisateur Notications Panneaux dalerte Localisation Vues personnalises Images et vnements de la souris vnements du clavier Attributs de texte Presse-papiers et actions sur nil Catgories Glisser-dposer NSTimer Feuilles NSFormatter Impression Services web change de vues Relations Core Data Ramasse-miettes Bases de lanimation Application Cocoa/OpenGL simple NSTask Final

Programmation

Niveau : Intermdiaire Conguration : Mac OS 10.4 et 10.5

propos de lauteur : Aaron Hillegass, qui a travaill pour NeXT et Apple, donne prsent des cours trs rputs de programmation avec Cocoa au Big Nerd Ranch. Lors de son passage chez NeXT, il avait dj crit le premier cours sur OpenStep, le prdcesseur de Cocoa.

Pearson Education France 47 bis, rue des Vinaigriers 75010 Paris Tl. : 01 72 74 90 00 Fax : 01 42 05 22 17 www.pearson.fr

ISBN : 978-2-7440-4093-1

You might also like