Practicant amb Objective-CDoncs ara passarem a fer una mica de programació amb Objective-C per posar-nos al dia de la seva sintaxi i gramà tica. També mirarem de veure com funcionen les caracterÃstiques principals d'aquest llenguatge i cap al final explicarem un parell de classes de framework Foundation. Aquest tutorial està basat en el que es troba en aquesta pà gina. He escollit aquest pel fet de ser senzill i perquè hi he vist exemples. ComençantOrÃgen d'aquest tutorialCom ja he comentat en la breu explicació del principi, aquest tutorial està basat en el d'aquesta pà gina on el podeu trobar en anglès. Instal·lant l'entornPer seguir aquest tutoria podeu fer-ho des de diferents Sistemes Operatius, doncs l'Objective-C està disponible amb codi lliure i existeixen diferents IDEs per a cada plataforma. Encara que un servidor utilitzarà la IDE XCode d'Apple podeu fer-ho amb les següents.
PreambleAquest tutorial assumeix que tens alguns coneixement bà sics de C, incloent-hi els tipus de dades C, què és una funció, què és un valor de retorn, conèixer els punters i la gestió de memòria a C. Si no en tens massa, et recomanem que t'hi introdueixis abans de continuar. L'Objective-C, com a derivat de C, hereta totes les caracterÃstiques de C. Hi ha alguns excepcions però no es desvien massa del que ofereix C com a llenguatge. nil: En C/C++, probablement, has utilitzar NULL. A Objective-C aquest és diu nil. La diferència és que tu pot passar missatges a nil (per exemple BOOL: C no té un tipus oficial de booleà , i en realitat l'Objective-C tampoc. Està inclòs dins de les classes Foundation (anomenades amb la importació de NSObject.h). nil també està inclòs en el fitxer de capçalera. BOOL a Objective-C té dos estats. YES i NO en comptes de TRUE i FALSE.
Les paraules mètode i missatge s'utilitzen indistintament a Objective-C, encara que els missatges tenen propietats especials. Un missatge pot transmetre's dinà micament a un altre objecte. Cridar un missatge d'un objecte a Objective-C no significa que l'objecte implementa aquest missatge, només que aquest coneix com respondre-hi ja sigui implementant-lo directament o transmetent el missatge a una altre objecte que sap com fer-ho. Fent un Hola MónDoncs au, anem a fer el nostre primer programa. El primer que hem de fer és obrir la nostra IDE, en el cas de Mac OS X l'XCode que si l'heu instal·lat a Un cop el tenim obert l'XCode, només tenim un menú. Llavors passem a crear la nostra primera aplicació, creant un nou projecte. Aneu a File > New Project o premeu les tecles corresponents. Quan us surti l'assistent seleccioneu Command Line Utility i dins seu Foundation Tool. Després d'indicar el nom del projecte i el directori on es guardarà un oferirà una sèrie de fitxers amb un en particular que conté el codi. Exactament és el fitxer HolaMon.m depenent del nom que hi heu ficat al projecte. El codi d'aquest fitxer és el següent: #import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
NSLog(@"Hola, Món!");
[pool release];
return 0;
}
Bé, ara només cal que canvieu el text al català . ;-). Si el compilleu i l'executeu us sortirà una finestra amb els Logs del programa, i el missatge que hi heu ficat. [Session started at 2005-08-20 13:00:56 +0200.] 2005-08-20 13:00:56.171 HolaMon[730] Hola, Món! Executable “Hola Mónâ€? has exited with status 0. Però ja sé que em direu que això només funciona amb l'XCode, i en part teniu raó, dons l'XCode executa el compilador amb els parà metres adients d'on agafar les llibreries i el fitxer resultant de la compilació. Clar que també podem mirar de fer-ho a mà des de la lÃnia de comandes. Primer de tot executarem l'apliació des de la lÃnia de comandes per comprovar que hem fet un programa de lÃnia de comandes. Anem al directori del projecte i entrem en el directori build. A dins ja podem executar el programa ./HolaMon i ens retorna el següent: 2005-08-20 13:10:10.538 HolaMon[768] Hola, Món! El següent pas és aconseguir compilar el programa des de la lÃnia de comandes, per això comencem provant sort per si funciona 2005-08-20 14:53:36.865 HolaMon[1235] Hola, Món! Si a algú li interessa saber on trobar les instruccions exactes que executa l'XCode ho pot mirar en el recuadre central del menú Build > Detailed Build Result quan aquest compili. Però pels que no teniu Cocoa, podeu fer la prova amb el codi següent, directament editant un fitxer #import <stdio.h>
int main( int argc, const char *argv[] ) {
printf( "Hola món\n" );
return 0;
}
I per compilar-lo, executeu Utilitzeu #import en comptes de #include a Objective-C Per defecte la extensió del fitxer d'Objective-C és .m Creant classesUn cop provat que podem compilar i executar un petit programa, ja estem preparats per introduir-nos a la programació orientada a objectes amb Objective-C. Per això crearem un projecte nou on implementarem una classe Fracció que ens permeti treballar les fraccions amb numerador i denominador. Us recomanem que feu una ullada al resum del llenguatge Objective-C per conèixer com funciona, i que el tingueu com a referència a mida que aneu desenvolupant programari. Creem el projecteDoncs som-hi, anem a l'XCode i creem un projecte nou de lÃnia de comandes anomenat AppFraccio, com en el projecte anterior. L'XCode ens tornarà a crear el codi de l'Hola Món anterior que ja ens encarregarem després d'esborrar o substituir. Creem la classePer crear una classe nova anem al menu arxiu > Nou fitxer con ens sortirà un assistent d'on seleccionarem dins l'arbre l'opció Cocoa > Objective-C class. Ens demanarà el nom de la classe que l'anomenarem Fraccio i el lloc on es guardarà i amb quines aplicacions es compilarà (en un projecte es poden tenir diferents destins de la construcció amb diferents classes a compilar). Deixem la resta de parà metres per defecte i premem el botó de Finalitzar. Podem comprovar que se'ns ha creat dos fitxers amb extensions //
// Fraccio.h
// AppFraccio
//
// Created by Xin Xic on 20/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface Fraccio : NSObject {
}
@end
Del codi anterior es pot observar diferents elements que descriurem en aquest parà graf. En primer lloc veiem uns comentaris amb el nom de la classe a quina aplicació pertany, qui l'ha creat i quan i alguns drets que poden haver-hi. Passant al codi, podem veure la primera directiva de pre-processador on es carrega el fitxer de capçalera de Cocoa. La següent lÃnia pertany a les Directives del Compilador que s'utilitza per declarar una classe nova, aquestes han d'acabar amb Per definir la classe es comença definir els missatges que ha d'atendre aquesta classe. En el nostre cas, la classe Fraccio atendrà missatges per definir el numerador i el denominador i també per obtindre'n els valors. (Encara que us pugui semblar estrany que es defineixin primer els missatges abans que les dades, té una lògica que està explicada en el ;anual de Programació Orientada a Objectes i posteriorment en la Guia de Patrons de Disseny amb Cocoa). Aixà doncs, comencem a definir els missatges que volem que atengui la nostra classe, afegint les següents lÃnies. ... - print; -(void) setNumerador: (int) n; -(void) setDenominador: (int) d; -(int) numerador; -(int) denominador; ... Aquestes funcions ens serviran per treure imprimir per la sortida d'error un cadena que representi la fracció. També tindrem dues funcions per establir el numerador i el denominador. I per acabar dues funcions que ens retornaran el numerador i el denominador. Recordeu que per defecte els mètodes dins Objective-C retornen un tipus De la definició dels mètodes podem extrapolar les variables d'instà ncia necessà ries per pode utilitzar aquests mètodes. Aixà amb dues variables enteres que representin el numerador i el denominador n'hi hauria prou. Aquesta definició es realitzarà entre les claus de la definició de la classe. ...
@interface Fraccio : NSObject {
int numerador;
int denominador;
}
...
Ara ja tenim la classe definida, el següent pas serà implementar-ne la funcionalitat. Per això agafarem el fitxer amb extensió
//
// Fraccio.m
// AppFraccio
//
// Created by Xin Xic on 20/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "Fraccio.h"
@implementation Fraccio
- print {
return [NSString stringWithFormat: @"%d/%d", numerador, denominador];
}
-(void) setNumerador: (int) n {
numerador = n;
}
-(void) setDenominador: (int) d {
denominador = d;
}
-(int) numerador {
return numerador;
}
-(int) denominador {
return denominador;
}
@end
No cal explicar massa cosa d'aquest codi. És evident el que fa. Potser val la pena esmentar la directiva Bé doncs, ara només cal comprovar que el codi de la nostra classe funciona correctament. Per això afegirem el codi main de l'apliació en que farem unes quantes operacions per comprovar-ne el seu funcionament.
#import <Foundation/Foundation.h>
#import "Fraccio.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem una instà ncia nova
Fraccio *frac = [[Fraccio alloc] init];
// Establim els valors
[frac setNumerador: 1];
[frac setDenominador: 3];
// Ho imprimim
NSLog(@"La fracció és: %@\n", [frac print]);
// Alliberem la memòria
[frac release];
[pool release];
return 0;
}
En el codi de l'aplicació creem una instà ncia de la classe Fracció, li assignem el numerador i el denominador i després n'imprimim el resultat per comprovar que a funcionat. Per acabar alliberem de la memòria la instà ncia de la classe Fraccio que em realitzat al principi. Penseu que amb Objective-C el qui crea una instà ncia és l'encarregat d'alliberar-la de la memòria. També veiem la classe NSAutoreleasePool que pot fer aquestes funcions automà ticament, però això ja ho explicarem més endavant. Analitzant més a fons el codi afegit, podem observar com es crea una instà ncia a Objective-C i com es criden els mètodes. En la creació de la instà ncia el primer que es fa és cridar la funció de classe Un cop compilats i executat el codi, el resultat és el següent: [Session started at 2005-08-22 10:40:24 +0200.] 2005-08-22 10:40:24.496 AppFraccio[697] La fracció és: 1/3 Executable “AppFraccioâ€? has exited with status 0. Els detalls...En aquest apartat, entrarem a treballar amb altres conceptes dins de les classes que ens acabaran de donar forma al treball bà sic amb les classes. Parà metres MúltiplesFins aquest punt, no s'ha mostrat cap forma especÃfica per definir parà metres múltiples. En principi no és intuïtiu, ja que prové de la sintaxi de l'Smalltalk. Per definir múltiples parà metres en un mètode ho podeu veure a l'afegir al codi de la classe Fraccio un mètode per introduir el numerador i el denominador en un únic mètode. ... -(void) setNumerador: (int) n iDenominador: (int) d; ... ...
-(void) setNumerador: (int) n iDenominador: (int) d {
numerador = n;
denominador = d;
}
...
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem una instà ncia nova
Fraccio *frac = [[Fraccio alloc] init];
Fraccio *frac2 = [[Fraccio alloc] init];
// Establim els valors
[frac setNumerador: 1];
[frac setDenominador: 3];
[frac2 setNumerador: 1 iDenominador: 5];
// Ho imprimim
NSLog(@"La fracció és: %@\n", [frac print]);
NSLog(@"La fracció 2 és: %@\n", [frac2 print]);
// Alliberem la memòria
[frac release];
[frac2 release];
[pool release];
return 0;
}
Comprovant el funcionament del nou codi ens retorna aquesta soritda: [Session started at 2005-08-22 11:04:00 +0200.] 2005-08-22 11:04:00.536 AppFraccio[719] La fracció és: 1/3 2005-08-22 11:04:00.538 AppFraccio[719] La fracció 2 és: 1/5 Executable “AppFraccio� has exited with status 0. Fixant-nos amb el codi afegit, veiem que per a cada nou parà metre s'han d'afegir dos punts, on opcionalment es pot indicar una descripció que formaria part del nom del mètode. Aixà un mètode amb 4 parà metres pot definir-se amb un descripció com aquest ConstructorsEl constructor a Objective-C estan implementats sobre les funcions que comencen amb -(Fraccio*) initAmbNumerador: (int) n iDenominador: (int) d;
-(Fraccio*) initAmbNumerador: (int) n iDenominador: (int) d {
self = [super init];
if (self) {
[self setNumerador: n iDenominador: d];
}
return self;
}
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem una instà ncia nova
Fraccio *frac = [[Fraccio alloc] init];
Fraccio *frac2 = [[Fraccio alloc] init];
Fraccio *frac3 = [[Fraccio alloc] initAmbNumerador: 3 iDenominador: 10];
// Establim els valors
[frac setNumerador: 1];
[frac setDenominador: 3];
[frac2 setNumerador: 1 iDenominador: 5];
// Ho imprimim
NSLog(@"La fracció és: %@\n", [frac print]);
NSLog(@"La fracció 2 és: %@\n", [frac2 print]);
NSLog(@"La fracció 3 és: %@\n", [frac3 print]);
// Alliberem la memòria
[frac release];
[frac2 release];
[frac3 release];
[pool release];
return 0;
}
En el nou codi podem veure alguns conceptes nous, com per exemple la paraula Tingueu en compte que si self és Un cop compilat i executat ens retorna el següent resultat: [Session started at 2005-08-22 11:29:35 +0200.] 2005-08-22 11:29:36.017 AppFraccio[825] La fracció és: 1/3 2005-08-22 11:29:36.018 AppFraccio[825] La fracció 2 és: 1/5 2005-08-22 11:29:36.018 AppFraccio[825] La fracció 3 és: 3/10 Executable “AppFraccio� has exited with status 0. Privilegis d'AccésCom hem comentat anteriorment l'accés per defecte és Acces.h #import Acces.m #import "Acces.h" @implementation Acces @end AppAcces.m
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem la instà ncia
Acces *a = [[Acces alloc] init];
// treballem-hi
a->publicVar = 5;
NSLog(@"variable pública: %d\n", a->publicVar);
// no compila
a->privateVar = 10;
NSLog(@"variable privada: %d\n", a->privateVar);
// no compila
a->protectedVar = 15;
NSLog(@"variable protegida: %d\n", a->protectedVar);
[a release];
[pool release];
return 0;
}
Les lÃnies amb les variables privades i protegides no retornen error al compilar-se, però adverteixen que en un futur no s'hi podrà accedir i donarà error. D'altra banda, treballar amb punters no és la forma que s'ha de fer amb Objective-C, en teoria no es bona prà ctica accedir a les variables d'una classe directament, sempre s'ha de fer utilitzant funcions, doncs com hem dit anteriorment el que defineix una classe no són els seus parà metres sinó els missatge que pot respondre, i en un moment un classe pot tenir una variable i en un futur desaparèixer al estar implÃcita en una altra, i això comportaria un error en el codi. Si s'utilitzen funciona per retornar valors, sempre es pot calcular el valor buscat de qualsevol lloc, no cal tenir la variable. Tot això s'explicarà més endavant. Resumint, els privilegis d'accés de les variables indica la visibilitat respecte altres classes, aixà una variable publica hi pot accedir tothom, una de protegida totes les classes que hi hereten i les privades solament la pròpia classe. Però amb el comentat abans, s'ha de mirar d'evitar accedir directament a variables d'una altra classe, sempre és millor fer-ho mitjançant missatges de petició. Nivell d'accés de ClasseEn punts anteriors hem comentat per sobre el sÃmbol (-) que hi ha davant dels mètodes de les classes, i hem introduït la possibilitat de canviar aquest sÃmbol per un (+). Doncs bé, aquesta caracterÃstica de l'Objective-C és la forma com aquest permet crear funcions està tiques, que utilitza el C++. Però l'Objective-C no treballa exactament com el C++. A l'Objective-C quan es defineix una classe i s'utilitza paral·lelament crea una instà ncia d'objecte ClasseA.h
//
// ClasseA.h
// AppClasse
//
// Created by Xin Xic on 23/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
static int comptador;
@interface ClasseA : NSObject {
}
+(int) comptador;
+(void) initialize;
@end
ClasseA.m
//
// ClasseA.m
// AppClasse
//
// Created by Xin Xic on 23/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "ClasseA.h"
@implementation ClasseA
-(id) init {
self = [super init];
comptador++;
return self;
}
+(int) comptador {
return comptador;
}
+(void) initialize {
comptador = 0;
}
@end
AppClasse.m
#import <Foundation/Foundation.h>
#import "ClasseA.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem les instà ncies
ClasseA *c1 = [[ClasseA alloc] init];
ClasseA *c2 = [[ClasseA alloc] init];
// Imprimim el comptador
NSLog(@"Comptador ClasseA: %d\n", [ClasseA comptador]);
// Inicialitzem una altra instà ncia
ClasseA *c3 = [[ClasseA alloc] init];
// Imprimim altra vegada el comptador
NSLog(@"Comptador ClasseA: %d\n", [ClasseA comptador]);
// Alliberem les classes de la memòria
[c1 release];
[c2 release];
[c3 release];
[pool release];
return 0;
}
Anem a analitzar el codi que hem escrit, començant per les declaracions de la implementació de la ClasseA. El primer que cal esmentar és la funció de Classe La funció principal de la aplicació crea en un primer moment dues instà ncies de la ClasseA i en mostra el comptador d'instà ncies i després en crea una altra i en torna a mostrar el comptador per comprovar que aquest funciona correctament. Al final s'alliberen les instà ncies de la memòria. El resultat de la compilació i execució és el següent: [Session started at 2005-08-23 10:22:12 +0200.] 2005-08-23 10:22:12.613 AppClasse[751] Comptador ClasseA: 2 2005-08-23 10:22:12.613 AppClasse[751] Comptador ClasseA: 3 Executable “AppClasse� has exited with status 0. ExcepcionsEl llenguatge Objective-C té una sintaxi de captura d'exepcions similar a la que té Java i C++ amb l'us de les classes NSException, NSError, o de pròpies, pots afegir una robusta captura d'excepcions en els teus programes. El suport d'excepcions giren al voltant de quatre directives del compilador: @try, @catch, @throw i @finally. El codi que potencialment pot llençar una exceptió està tancat per un block @try. Un block @finally conté el codi que ha d'executar-se si un excepció és llença o no. Utilitza la directiva @throw per llançar una excepció, que és essencialment un punter a un objecte Objective-C. Pots utilitzar els objectes NSException però no estan limitades a ells. Podeu veure-ho més extensament aquà Per veure una mica com funcionen les excepcions anem a escriure un exemple i explicar-lo. Per fer això crearem un projecte nou anomenat AppCopa on hi haurà una classe normal on hi haurà excepcions i un parell de classe d'excepció de diferent tipus per comprovar-ne el funcionament. CopaUnderflowException.h
//
// CopaUnderflowException.h
// AppCopa
//
// Created by Xin Xic on 23/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface CopaUnderflowException : NSException {
}
@end
CopaUnderflowException.m // // CopaUnderflowException.m // AppCopa // // Created by Xin Xic on 23/08/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import "CopaUnderflowException.h" @implementation CopaUnderflowException @end CopaWarningException.h
//
// CopaWarningException.h
// AppCopa
//
// Created by Xin Xic on 23/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface CopaWarningException : NSException {
}
@end
CopaWarningException.m // // CopaWarningException.m // AppCopa // // Created by Xin Xic on 23/08/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import "CopaWarningException.h" @implementation CopaWarningException @end CopaOverflowException.h
//
// CopaOverflowException.h
// AppCopa
//
// Created by Xin Xic on 23/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface CopaOverflowException : NSException {
}
@end
CopaOverflowException.m // // CopaOverflowException.m // AppCopa // // Created by Xin Xic on 23/08/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import "CopaOverflowException.h" @implementation CopaOverflowException @end Copa.h
//
// Copa.h
// AppCopa
//
// Created by Xin Xic on 23/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface Copa : NSObject {
int nivell;
}
-(int) nivell;
-(void) setNivell: (int) n;
-(void) omple;
-(void) buida;
-(id) print;
@end
Copa.m
//
// Copa.m
// AppCopa
//
// Created by Xin Xic on 23/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "Copa.h"
#import "CopaOverflowException.h"
#import "CopaWarningException.h"
@implementation Copa
-(id) init {
self = [super init];
if ( self ) {
[self setNivell: 0];
}
return self;
}
-(int) nivell {
return nivell;
}
-(void) setNivell: (int) n {
nivell = n;
if ( nivell > 100 ) {
// throw overflow
NSException *e = [CopaOverflowException
exceptionWithName: @"CopaOverflowException"
reason: @"El nivell està per sobre de 100"
userInfo: nil];
@throw e;
} else if ( nivell >= 50 ) {
// throw warning
NSException *e = [CopaWarningException
exceptionWithName: @"CopaWarningException"
reason: @"El nivell està per sobre de 50"
userInfo: nil];
@throw e;
} else if ( nivell < 0 ) {
// throw exception
NSException *e = [NSException
exceptionWithName: @"CopaUnderflowException"
reason: @"El nivell està per sota de 0"
userInfo: nil];
@throw e;
}
}
-(void) omple {
[self setNivell: nivell + 10];
}
-(void) buida {
[self setNivell: nivell - 10];
}
-(id) print {
return [NSString stringWithFormat: @"El nivell de la Copa és: %d\n", nivell ];
}
@end
AppCopa.m
#import <Foundation/Foundation.h>
#import "Copa.h"
#import "CopaUnderflowException.h"
#import "CopaWarningException.h"
#import "CopaOverflowException.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem una instà ncia de la copa.
Copa *copa = [[Copa alloc] init];
int i;
// Això és el que farÃ
for ( i = 0; i < 4; i++ ) {
[copa omple];
NSLog([copa print]);
}
// Això capturarà les excepcions
NSString *excep;
for ( i = 0; i < 7; i++ ) {
@try {
[copa omple];
} @catch ( CopaUnderflowException *e ) {
excep = [e name];
} @catch ( CopaWarningException *e ) {
excep = [e name];
} @catch ( CopaOverflowException *e ) {
excep = [e name];
} @finally {
NSLog(@"%@: %@", excep, [copa print]);
}
}
// Llença un excepció genèrica
@try {
[copa setNivell: -1];
} @catch ( NSException *e ) {
NSLog( @"%@: %@\n", [e name], [e reason] );
}
// alliberem memòria
[copa release];
[pool release];
return 0;
}
Primer de tot cal destacar que les classes d'excepcions hereten totes elles de NSException, és obligatori si volem que sigui una excepció. L'altre cosa a destacar és que no tenen cap codi, doncs en el nostre cas no els hi volem donar més funcionalitat de la que té NSException i només ens interessa diferenciar els diferents tipus d'excepcions. Un altre punt a destacar és el fet que nosaltres em d'encarregar-nos de decidir on es capturen les excepcions, sinó ho farà al final l'aplicació i potser no en podrem controlar el resultat (possiblement ens tanqui l'apliació), per capturar una excepció s'ha de tancar entre un Passant a explicar el que fa el programa, primer parlem de la classe Copa. Aquesta s'encarrega de variar el seu nivell omplint-lo i buidant-lo de deu en deu, i un cop analitzat el nivell llença les excepcions adients a l'estat causat. El codi de l'aplicació crea una copa que va omplint de mica en mica. Primer incrementa el seu nivell 4 vegades fins al 40% i no es preocupa de capturar cap impressió (si augmenteu el nombre de cops de s'incrementa el nivell de la copa sense capturar la excepció veureu que com que no es tracta l'excepció l'aplicació finalitza el programa) 2005-08-23 13:10:25.446 AppCopa[1500] *** Uncaught exception: Però be, continuem amb el nostre programa. Un cop omplert fins al 40% es torna a repetir l'operació fins al 110% però aquest cop mirant de capturar les possibles excepcions que es produiran. Per acabar es deixa el nivell de la copa per sota de zero i també es captura l'excepció, en aquest cas sense diferenciar el codi d'aquesta excepció, doncs només se'n vol imprimir el nom i la raó que l'ha causat. Executant normalment l'aplicació retorna la següent sortida: [Session started at 2005-08-23 13:14:24 +0200.] 2005-08-23 13:14:24.495 AppCopa[1512] El nivell de la Copa és: 10 2005-08-23 13:14:24.523 AppCopa[1512] El nivell de la Copa és: 20 2005-08-23 13:14:24.540 AppCopa[1512] El nivell de la Copa és: 30 2005-08-23 13:14:24.557 AppCopa[1512] El nivell de la Copa és: 40 2005-08-23 13:14:24.587 AppCopa[1512] CopaWarningException: El nivell de la Copa és: 50 2005-08-23 13:14:24.605 AppCopa[1512] CopaWarningException: El nivell de la Copa és: 60 2005-08-23 13:14:24.621 AppCopa[1512] CopaWarningException: El nivell de la Copa és: 70 2005-08-23 13:14:24.637 AppCopa[1512] CopaWarningException: El nivell de la Copa és: 80 2005-08-23 13:14:24.652 AppCopa[1512] CopaWarningException: El nivell de la Copa és: 90 2005-08-23 13:14:24.672 AppCopa[1512] CopaWarningException: El nivell de la Copa és: 100 2005-08-23 13:14:24.688 AppCopa[1512] CopaOverflowException: El nivell de la Copa és: 110 2005-08-23 13:14:24.688 AppCopa[1512] CopaUnderflowException: El nivell està per sota de 0 Executable “AppCopaâ€? has exited with status 0. Tingueu en compte que s'ha hagut d'habilitar l'opció d'excepcions amb Objective-C del compilador, que no la tenia per defecte. Herència, Polimorfisme i altres caracterÃstiques de la POOEn aquest apartat començarem a ficar-nos dins la Programació Orientada a Objectes (POO) que ens ofereix l'Objective-C. En aquest enllaç es parla sobre la POO des d'una visió de l'Objective-C i Cocoa. El tipus idPer començar parlarem del tipus En aquest primer exemple utilitzarem el codi que ja teniem de l'Applicació AppFraccio, i per veure una altre sistema de crear diferents executables amb XCode a partir del mateix codi, crearem un nou "target" per estalviar-nos modificar l'aplicació anterior i/o copiar algunes classes, com poden ser la classe Fraccio. Per crear un executable alternatiu seguirem els següents passos. Primer de tot obrim el projecte base anterior AppFracio. Anem a la opció dels menus "Project > New Target", d'on ens sortirà un assistent que ens permetrà escollir quin tipus d'executamble volem crear. En el nostre cas sel·leccionarem "Cocoa > Shell Tool" i li ficarem com a nom de l'aplicació AppComplex (doncs també haurem de crear aquesta classe). Ara ens apareix a l'arbre de navegació de l'XCode un nou target anomenat AppComplex. Ara cal afegir quins arxius es compilaran en aquest nou executable. Això és tant senzill com desplaçar els fitxers que volem a dins de "Targets > AppComplex > Sources". També ens caldrà afegir les frameworks necessà ries per que compili correctament, doncs fem-ho directament de la framework del codi i la enviem amb el ratolà cap a "Targets > AppComplex > Frameworks & Libraries". Podeu crear més "Fases de Construcció" des del menú contextual "Add > New Build Phase" i sel·leccionar el que necessitis. També ho pots fer des del menú "Project" principal. Si us hi fixeu, un dels targets té un sÃmbol verd d'acceptat que indica quin és el target actual. Podem canviar-ho i fer que AppComplex sigui l'actual, només cal anar a l'opció "Active Target" de la barra d'eines i seleccionar AppComplex. Doncs ara només cal afegir els fitxers i classes noves dins d'aquest nou target. La primera d'aquestes serà la classe Complex que representarà a un número complexe (real, imaginari) i que l'assignarem al nou target. Si us hi fixeu quan creem una nova classe ens demana a quins targets es compilarà , podeu veure que el fitxer complex.m s'ha afegit a "Targets > AppComplex > Sources". I ja està , ara només cal escriure el codi. Complex.h
//
// Complex.h
// AppFraccio
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface Complex : NSObject {
double real;
double imaginari;
}
// Init's
-(Complex*) initAmbReal: (double) r iImaginari: (double) i;
// Set's
-(void) setReal: (double) r;
-(void) setImaginari: (double) i;
-(void) setReal: (double) r iImaginari: (double) i;
// Get's
-(double) real;
-(double) imaginari;
// Altres Funcions
-(id) print;
@end
Complex.m
//
// Complex.m
// AppFraccio
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "Complex.h"
@implementation Complex
// Init's
-(Complex*) initAmbReal: (double) r iImaginari: (double) i {
self = [super init];
if (self) {
[self setReal: r iImaginari: i];
}
return self;
}
// Set's
-(void) setReal: (double) r {
real = r;
}
-(void) setImaginari: (double) i {
imaginari = i;
}
-(void) setReal: (double) r iImaginari: (double) i {
real = r;
imaginari = i;
}
// Get's
-(double) real {
return real;
}
-(double) imaginari {
return imaginari;
}
// Altres funciona
-(id) print {
return [NSString stringWithFormat: @"( %f + %fi )", real, imaginari];
}
@end
I per acabar hem de crear el fitxer amb la funció AppComplex.m
#import <Foundation/Foundation.h>
#import "Fraccio.h"
#import "Complex.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem una instà ncia nova
Fraccio *frac = [[Fraccio alloc] initAmbNumerador: 1 iDenominador: 10];
Complex *comp = [[Complex alloc] initAmbReal: 10 iImaginari: 15];
id nombre;
// Imprimim la fracció
nombre = frac;
NSLog(@"La fracció és: %@\n", [nombre print]);
// Imprimim el nombre complex
nombre = comp;
NSLog(@"El nombre complex és: %@\n", [nombre print]);
// Alliberem la memòria
[frac release];
[comp release];
[pool release];
return 0;
}
Un cop compilat i executat aquest codi, ens retorna la següent sortida, comprovant que l'enviament de missatges a Objective-C no es comprova a l'hora de la compilació sinó en el moment de l'execució. [Session started at 2005-08-27 12:33:25 +0200.] 2005-08-27 12:33:26.193 AppComplex[788] La fracció és: 1/10 2005-08-27 12:33:26.194 AppComplex[788] El nombre complex és: ( 10.000000 + 15.000000i ) Executable “AppComplexâ€? has exited with status 0. També podeu comprovar l'error que es produeix i no existeix una de les funcions canviat el nom de la funció "print" de la classe Complex a "imprimir" i compileu-lo (sense cap problema) i executeu-lo (donant un error) [Session started at 2005-08-27 12:42:52 +0200.] 2005-08-27 12:42:52.070 AppComplex[802] La fracció és: 1/10 2005-08-27 12:42:52.071 AppComplex[802] *** -[Complex print]: selector not recognized 2005-08-27 12:42:52.072 AppComplex[802] *** Uncaught exception: Hi ha beneficis obvis en aquest tipus d'enllaçat dinà mic. No tens que conèixer el tipus d'un objecte per enviar-li un missatge (cridar un mètode). Si l'objecte respon al missatge, aquest invocarà aquest mètode. HerènciaL'herència és un dels mecanismes estrella de la programació orientada a objectes. Permet aprofitar codi estenent-ne la funcionalitat mitjançant l'herència. Tota la teoria sobre l'herència i quan utilitzar-la ho podeu trobar més extensament aquÃ. L'herència a Objective-C és semblant a Java. Quan extens la teva super-classe (de tal manera que només tens un sol pare) pots sobre-escriure els mètodes de la teva super-classe simplement posant noves implementacions en la implementació de la classe filla. No ho confongueu amb les funcions virtuals de C++. Per comprovar com funciona la herència realitzarem un nou projecte amb un parell de classes que representen un rectangle i un quadrat. Per això crearem un nou projecte que anomenarem AppHerencia. Rectangle.h
//
// Rectangle.h
// AppHerencia
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface Rectangle : NSObject {
int amplada;
int alsada;
}
// Init's
-(Rectangle*) initAmbAmplada: (int) w iAlsada: (int) h;
// Set's
-(void) setAmplada: (int) w;
-(void) setAlsada: (int) h;
-(void) setAmplada: (int) w iAlsada: (int) h;
// Get's
-(int) amplada;
-(int) alsada;
// Altres funcions
-(id) print;
@end
Rectangle.m
//
// Rectangle.m
// AppHerencia
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "Rectangle.h"
@implementation Rectangle
// Init's
-(Rectangle*) initAmbAmplada: (int) w iAlsada: (int) h {
self = [super init];
if ( self ) {
[self setAmplada: w iAlsada: h];
}
return self;
}
// Set's
-(void) setAmplada: (int) w {
amplada = w;
}
-(void) setAlsada: (int) h {
alsada = h;
}
-(void) setAmplada: (int) w iAlsada: (int) h {
amplada = w;
alsada = h;
}
// Get's
-(int) amplada {
return amplada;
}
-(int) alsada {
return alsada;
}
// Altres funciona
-(id) print {
return [NSString stringWithFormat:@"Amplada: %d, Alçada: %d", amplada, alsada];
}
@end
Quadrat.h
//
// Quadrat.h
// AppHerencia
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "Rectangle.h"
@interface Quadrat : Rectangle {
}
// Init's
-(Quadrat*) initAmbDimensio: (int) d;
// Set's
-(void) setDimensio: (int) d;
// Get's
-(int) dimensio;
@end
Quadrat.m
//
// Quadrat.m
// AppHerencia
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "Quadrat.h"
@implementation Quadrat
// Init's
-(Quadrat*) initAmbDimensio: (int) d {
self = [super init];
if (self ) {
[self setDimensio: d];
}
return self;
}
// Set's
-(void) setDimensio: (int) d {
amplada = d;
alsada = d;
}
-(void) setAmplada: (int) w {
[self setDimensio: w];
}
-(void) setAlsada: (int) h {
[self setDimensio: h];
}
// Get's
-(int) dimensio {
return amplada;
}
@end
AppHerencia.m
#import <Foundation/Foundation.h>
#import "Rectangle.h"
#import "Quadrat.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem les instà ncies
Rectangle *rec = [[Rectangle alloc] initAmbAmplada: 10 iAlsada: 20];
Quadrat *qua = [[Quadrat alloc] initAmbDimensio: 15];
// Els imprimim
NSLog(@"Rectangle: %@\n", [rec print]);
NSLog(@"Quadrat: %@\n", [qua print]);
// Actualitzem el quadrat
[qua setDimensio: 20];
NSLog(@"Quadrat després del canvi: %@\n", [qua print]);
// Alliberem la memòria
[rec release];
[qua release];
[pool release];
return 0;
}
Un compilada i executada l'aplicació el resultat obtingut serà el següent: [Session started at 2005-08-27 14:41:17 +0200.] 2005-08-27 14:41:17.330 AppHerencia[941] Rectangle: Amplada: 10, Alçada: 20 2005-08-27 14:41:17.332 AppHerencia[941] Quadrat: Amplada: 15, Alçada: 15 2005-08-27 14:41:17.332 AppHerencia[941] Quadrat després del canvi: Amplada: 20, Alçada: 20 Executable “AppHerencia� has exited with status 0. Que passaria si intentes cridar el constructor del rectangle utilitzant la classe Quadrat Tipus dinà micHi ha diverses formes de treballar amb tipus dinà mics a Objective-C
Cada objecte heretat de NSObject té un mètode de classe que retorna l'objecte de la classe. Aquest és molt semblant al mètode AppDinamic: El resultat és el següent: [Session started at 2005-08-27 16:44:11 +0200.] 2005-08-27 16:44:11.842 AppDinamic[1090] el quadrat és un membre de la classe Quadrat 2005-08-27 16:44:11.843 AppDinamic[1090] el quadrat és un Quadrat 2005-08-27 16:44:11.843 AppDinamic[1090] el quadrat és un Rectangle 2005-08-27 16:44:11.843 AppDinamic[1090] el quadrat és un NSObject 2005-08-27 16:44:11.843 AppDinamic[1090] el quadrat respon al mètode setDimensio 2005-08-27 16:44:11.843 AppDinamic[1090] el quadrat respon al mètode alloc 2005-08-27 16:44:11.843 AppDinamic[1090] el quadrat respon al mètode setDimensio Executable “AppDinamic� has exited with status 0. Podeu comprovar que la instà ncia "qua" no és una instà ncia d'un Rectangle. Que "qua" és un Quadrat, un Rectangle i un NSObject. I també comprovem com funcionen els selectors. CategoriesQuan vols afegir mètodes a una classe, normalment n'heretes. Tanmateix aquesta solució no és sempre perfecte, especialment si vols re-escriure la funcionalitat d'una classe de la que no tens el codi font. Les categories et permeten afegir funcionalitat a classes ja existents sense heretar-les. Ruby també té una funcionalitat semblant a aquesta. En el següent exemple se'n pot comprovar el funcionament. Partint de l'exemple de la fracció crearem un nou target AppMatematica on hi afegirem noves funcions mitjançant una categoria. FraccioMatematica.h // // FraccioMatematica.h // AppFraccio // // Created by Xin Xic on 27/08/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import <Cocoa/Cocoa.h> #import "Fraccio.h" @interface Fraccio (Matematica) // Operacions matemà tiques amb fraccions -(Fraccio*) sum: (Fraccio*) f; -(Fraccio*) mul: (Fraccio*) f; -(Fraccio*) div: (Fraccio*) f; -(Fraccio*) res: (Fraccio*) f; @end FraccioMatematica.m
//
// FraccioMatematica.m
// AppFraccio
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "FraccioMatematica.h"
@implementation Fraccio (Matematica)
// Operacions matemà tiques amb fraccions.
-(Fraccio*) sum: (Fraccio*) f {
return [[Fraccio alloc] initAmbNumerador: numerador * [f denominador] + denominador * [f numerador]
iDenominador: denominador * [f denominador]];
}
-(Fraccio*) mul: (Fraccio*) f {
return [[Fraccio alloc] initAmbNumerador: numerador * [f numerador]
iDenominador: denominador * [f denominador]];
}
-(Fraccio*) div: (Fraccio*) f {
return [[Fraccio alloc] initAmbNumerador: numerador * [f denominador]
iDenominador: denominador * [f numerador]];
}
-(Fraccio*) res: (Fraccio*) f {
return [[Fraccio alloc] initAmbNumerador: numerador * [f denominador] - denominador * [f numerador]
iDenominador: denominador * [f denominador]];
}
@end
AppMatematica.h
#import <Foundation/Foundation.h>
#import "Fraccio.h"
#import "FraccioMatematica.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem una instà ncia nova
Fraccio *frac1 = [[Fraccio alloc] initAmbNumerador: 1 iDenominador: 3];
Fraccio *frac2 = [[Fraccio alloc] initAmbNumerador: 2 iDenominador: 5];
Fraccio *frac3 = [frac1 mul: frac2];
// Ho Imprimim
NSLog(@"%@ * %@ = %@\n", [frac1 print], [frac2 print], [frac3 print]);
// Alliberem la memòria
[frac1 release];
[frac2 release];
[frac3 release];
[pool release];
return 0;
}
El resultat és el següent: [Session started at 2005-08-27 19:36:58 +0200.] 2005-08-27 19:36:58.749 AppMatematica[1291] 1/3 * 2/5 = 2/15 Executable “AppMatematica� has exited with status 0.
Aquà hi ha dues lÃnies mà giques Les categories no poden tenir variables d'instà ncia. S'utilitzen normalment per crear mètodes privats. Com que l'Objective-C no te noció de mètode privats/protegits/publics com fa el Java, s'han de crear categories per amagar la funcionalitat. La forma de fer això és movent els mètodes privats des del fitxer de capçalera de la classe (.h) al fitxer d'implementació (.m) utilitzant una Categoria. Amb el següent exemple ho entendreu: LaMevaClasse.h
//
// LaMevaClasse.h
// AppCategoriaPresentacio
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface LaMevaClasse : NSObject {
}
-(id) metodePublic;
@end
LaMevaClasse.m
//
// LaMevaClasse.m
// AppCategoriaPresentacio
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "LaMevaClasse.h"
@implementation LaMevaClasse
-(id) metodePublic {
return @"Mètode Públic";
}
@end
// mètodes privats
@interface LaMevaClasse (Privat)
-(id) metodePrivat;
@end
@implementation LaMevaClasse (Privat)
-(id) metodePrivat {
return @"Mètode Privat";
}
@end
AppCategoria.h
#import <Foundation/Foundation.h>
#import "LaMevaClasse.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem la instà ncia de la classe.
LaMevaClasse *obj = [[LaMevaClasse alloc] init];
// Això funciona
NSLog([obj metodePublic]);
// Això llança un erro quan compila
//NSLog([obj metodePrivat]);
// Alliberem la memòria
[obj release];
[pool release];
return 0;
}
PresentacióLa Presentació és semblant a les categories, però amb una variació. Et permet estendre una classe, i fer que la seva classe es mostri globalment (en lloc de) la super-classe. Per exemple: Posem que tens un NSArrayChils que estén de NSArray. Si fas presentació de NSArrayChils per NSArray tot el teu codi que comenci utilitzarà instà ncies de NSArrayChild instanciades de NSArray automà ticament. Mirem-ho amb un exemple a partir del projecte Fraccio: FraccioB.h // // FraccioB.h // AppFraccio // // Created by Xin Xic on 27/08/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import <Cocoa/Cocoa.h> #import "Fraccio.h" @interface FraccioB : Fraccio -(id) print; @end FraccioB.m
//
// FraccioB.m
// AppFraccio
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "FraccioB.h"
@implementation FraccioB
-(id) print {
return [NSString stringWithFormat: @"(%d / %d)", numerador, denominador ];
}
@end
AppFraccioB.h
#import <Foundation/Foundation.h>
#import "Fraccio.h"
#import "FraccioB.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem una instà ncia nova
Fraccio *frac = [[Fraccio alloc] initAmbNumerador: 3 iDenominador: 10];
// Imprimir-ho
NSLog(@"La fracció és: %@\n", [frac print]);
// Fem que FraccioB sigui la presentació de Fraccio
[FraccioB poseAsClass: [Fraccio class]];
// Creem una nova instà ncia nova
Fraccio *frac2 = [[Fraccio alloc] initAmbNumerador: 3 iDenominador: 10];
// Imprimir-ho
NSLog(@"La fracció és: %@\n", [frac2 print]);
[frac release];
[frac2 release];
[pool release];
return 0;
}
La sortida d'aquest codi és el següent: [Session started at 2005-08-27 21:04:45 +0200.] 2005-08-27 21:04:46.189 AppFraccioB[1425] La fracció és: 3/10 2005-08-27 21:04:46.191 AppFraccioB[1425] La fracció és: (3 / 10) Executable “AppFraccioBâ€? has exited with status 0. La sortida del programa s'imprimeix el primer cop com 3/10. El següent cop com (3/10), el qual està implementat a FraccioB. El mètode poseAsClass és part de NSObject. Aquest permet a una sub-classe presentar-se en lloc super-classe. Realment és sorprenent aquesta funcionalitat. Amb quina facilitat pot canviar-se el funcionament d'una aplicació amb una sola instrucció. Només els avantatges que suposa a l'hora de comprovar alternatives és impressionant. ProtocolsUn protocol a l'Objective-C és idèntic en funcionalitat que una interfÃcie a Java, o una classe virtual pura a C++. Mirem aquest exemple basat en la classe Fraccio, encara que en farem de nou, per no espatllar els exemples ja realitzats. Imprimint.h /* * Imprimint.h * AppProtocol * * Created by Xin Xic on 01/09/05. * Copyright 2005 __MyCompanyName__. All rights reserved. * */ #import <Cocoa/Cocoa.h> @protocol Imprimint -(id) print; @end Fraccio.h
//
// Fraccio.h
// AppFraccio
//
// Created by Xin Xic on 20/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "Imprimint.h"
@interface Fraccio : NSObject <Imprimint, NSCopying> {
int numerador;
int denominador;
}
// Constructors
-(Fraccio*) initAmbNumerador: (int) n iDenominador: (int) d;
// Set's
-(void) setNumerador: (int) n;
-(void) setDenominador: (int) d;
-(void) setNumerador: (int) n iDenominador: (int) d;
// Get's
-(int) numerador;
-(int) denominador;
@end
Fraccio.m
//
// Fraccio.m
// AppFraccio
//
// Created by Xin Xic on 20/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "Fraccio.h"
@implementation Fraccio
-(Fraccio*) initAmbNumerador: (int) n iDenominador: (int) d {
self = [super init];
if (self) {
[self setNumerador: n iDenominador: d];
}
return self;
}
-(void) setNumerador: (int) n {
numerador = n;
}
-(void) setDenominador: (int) d {
denominador = d;
}
-(void) setNumerador: (int) n iDenominador: (int) d {
numerador = n;
denominador = d;
}
-(int) numerador {
return numerador;
}
-(int) denominador {
return denominador;
}
- print {
return [NSString stringWithFormat: @"%d/%d", numerador, denominador];
}
-(Fraccio*) copyWithZone: (NSZone*) zone {
return [[Fraccio allocWithZone: zone] initAmbNumerador: numerador
iDenominador: denominador];
}
@end
Complex.h
//
// Complex.h
// AppFraccio
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "Imprimint.h"
@interface Complex : NSObject <Imprimint> {
double real;
double imaginari;
}
// Init's
-(Complex*) initAmbReal: (double) r iImaginari: (double) i;
// Set's
-(void) setReal: (double) r;
-(void) setImaginari: (double) i;
-(void) setReal: (double) r iImaginari: (double) i;
// Get's
-(double) real;
-(double) imaginari;
@end
Complex.m
//
// Complex.m
// AppFraccio
//
// Created by Xin Xic on 27/08/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "Complex.h"
@implementation Complex
// Init's
-(Complex*) initAmbReal: (double) r iImaginari: (double) i {
self = [super init];
if (self) {
[self setReal: r iImaginari: i];
}
return self;
}
// Set's
-(void) setReal: (double) r {
real = r;
}
-(void) setImaginari: (double) i {
imaginari = i;
}
-(void) setReal: (double) r iImaginari: (double) i {
real = r;
imaginari = i;
}
// Get's
-(double) real {
return real;
}
-(double) imaginari {
return imaginari;
}
// Altres funciona
-(id) print {
return [NSString stringWithFormat: @"( %f + %fi )", real, imaginari];
}
@end
AppProtocol.h
#import <Foundation/Foundation.h>
#import "Fraccio.h"
#import "Complex.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem una instà ncia nova
Fraccio *frac = [[Fraccio alloc] initAmbNumerador: 3 iDenominador: 10];
Complex *comp = [[Complex alloc] initAmbReal: 5 iImaginari: 15];
id
Donant com a sortida: [Session started at 2005-09-01 16:22:19 +0200.] 2005-09-01 16:22:19.870 AppFraccio[1957] La fracció és: 3/10 2005-09-01 16:22:19.871 AppFraccio[1957] El nombre complex és: ( 5.000000 + 15.000000i ) 2005-09-01 16:22:19.871 AppFraccio[1957] La fracció conforma amb NSCopying Executable “AppFraccio� has exited with status 0. L'especificació del protocol és molt senzilla. És bà sicament Els mètode que el protocol requereix que s'implementin no calen que estiguin en la llista de mètodes del fitxer de capçalera. Com pots veure, Complex.h no té una definició per Gestió de MemòriaFins ara hem esquivat la gestió de la memòria a l'Objective-C. Segurament pots cridar a una funció Fixeu-vos que cada exemple que s'ha fet s'ha gestionar correctament la memòria, sense que t'ho hagis hagut de preguntar. Retenció i AlliberamentEls mètodes Mire-m'ho en un exemple: Fraccio.m
...
-(void) dealloc {
printf( "Esborrant una fraccio\n" );
[super dealloc];
}
...
AppMemoria.m
#import <Foundation/Foundation.h>
#import "Fraccio.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem una instà ncia nova
Fraccio *frac1 = [[Fraccio alloc] init];
Fraccio *frac2 = [[Fraccio alloc] init];
// Imprimim els comptadors actuals
NSLog(@"Comptador de referències de la fracció 1: %d\n", [frac1 retainCount]);
NSLog(@"Comptador de referències de la fracció 2: %d\n", [frac2 retainCount]);
// els incrementem
[frac1 retain]; // 2
[frac1 retain]; // 3
[frac2 retain]; // 2
// Imprimim els comptadors actuals
NSLog(@"Comptador de referències de la fracció 1: %d\n", [frac1 retainCount]);
NSLog(@"Comptador de referències de la fracció 2: %d\n", [frac2 retainCount]);
// els decrementem
[frac1 release]; // 2
[frac2 release]; // 1
// Imprimim els comptadors actuals
NSLog(@"Comptador de referències de la fracció 1: %d\n", [frac1 retainCount]);
NSLog(@"Comptador de referències de la fracció 2: %d\n", [frac2 retainCount]);
// Alliberem la memòria
[frac1 release];
[frac1 release];
[frac2 release];
[pool release];
return 0;
}
Retornant la següent sortida: [Session started at 2005-09-01 16:54:52 +0200.] 2005-09-01 16:54:52.102 AppMemoria[1989] Comptador de referències de la fracció 1: 1 2005-09-01 16:54:52.102 AppMemoria[1989] Comptador de referències de la fracció 2: 1 2005-09-01 16:54:52.102 AppMemoria[1989] Comptador de referències de la fracció 1: 3 2005-09-01 16:54:52.102 AppMemoria[1989] Comptador de referències de la fracció 2: 2 2005-09-01 16:54:52.103 AppMemoria[1989] Comptador de referències de la fracció 1: 2 2005-09-01 16:54:52.103 AppMemoria[1989] Comptador de referències de la fracció 2: 1 2005-09-01 16:54:52.103 AppMemoria[1989] Esborrant una fraccio 2005-09-01 16:54:52.103 AppMemoria[1989] Esborrant una fraccio Executable “AppMemoria� has exited with status 0. Les crides a DeallocQuan el teu objecte conté altres objectes, has d'alliberar-los quan tu mateix t'eliminis. Una dels bonics avantatges de l'Objective-C és que pots passar missatges a Mireu el següent exemple per entendre-ho millor: TargetaAdresa.h
//
// TargetaAdresa.h
// AppTargetaAdresa
//
// Created by Xin Xic on 01/09/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface TargetaAdresa : NSObject {
NSString *nom;
NSString *cognom;
NSString *correu;
}
// Init's
-(TargetaAdresa*) initAmbNom: (NSString*) n
cognom: (NSString*) c
iCorreu: (NSString*) e;
// Get's
-(NSString*) nom;
-(NSString*) cognom;
-(NSString*) correu;
// Set's
-(void) setNom: (NSString*) n;
-(void) setCognom: (NSString*) c;
-(void) setCorreu: (NSString*) e;
-(void) setNom: (NSString*) n
iCognom: (NSString*) c;
-(void) setNom: (NSString*) n
cognom: (NSString*) c
iCorreu: (NSString*) e;
// Altres
-(id) print;
@end
TargetaAdresa.m
//
// TargetaAdresa.m
// AppTargetaAdresa
//
// Created by Xin Xic on 01/09/05.
// Copyright 2005 __MyCompanyName__. All rights reserved.
//
#import "TargetaAdresa.h"
@implementation TargetaAdresa
// Init's
-(TargetaAdresa*) initAmbNom: (NSString*) n
cognom: (NSString*) c
iCorreu: (NSString*) e {
self = [super init];
if ( self ) {
[self setNom: n cognom: c iCorreu: e];
}
return self;
}
// Get's
-(NSString*) nom {
return nom;
}
-(NSString*) cognom {
return cognom;
}
-(NSString*) correu {
return correu;
}
// Set's
-(void) setNom: (NSString*) n {
[n retain];
[nom release];
nom = n;
}
-(void) setCognom: (NSString*) c {
[c retain];
[cognom release];
cognom = c;
}
-(void) setCorreu: (NSString*) e {
[e retain];
[correu release];
correu = e;
}
-(void) setNom: (NSString*) n
iCognom: (NSString*) c {
[self setNom: n];
[self setCognom: c];
}
-(void) setNom: (NSString*) n
cognom: (NSString*) c
iCorreu: (NSString*) e {
[self setNom: n];
[self setCognom: c];
[self setCorreu: e];
}
// Altres
-(id) print {
return [NSString stringWithFormat: @"%@ %@ <%@>", nom , cognom, correu];
}
// Sobre-escrites
-(void) dealloc {
[nom release];
[cognom release];
[correu release];
[super dealloc];
}
@end
AppTargetaAdresa.h
#import <Foundation/Foundation.h>
#import "TargetaAdresa.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem les dades
NSString *nom = [[NSString alloc] initWithFormat: @"Tom"];
NSString *cognom = [[NSString alloc] initWithFormat: @"Jones"];
NSString *correu = [[NSString alloc] initWithFormat: @"tom@jones.com"];
TargetaAdresa *tom = [[TargetaAdresa alloc] initAmbNom: nom
cognom: cognom
iCorreu: correu];
// ja no necessitem els strings, podem esborrar-los
[nom release];
[cognom release];
[correu release];
// imprimim per comprovar que s'ha retingut
NSLog(@"Contador: %d\n", [[tom nom] retainCount]);
NSLog(@"%@\n", [tom print]);
// Alliberem la memòria
[tom release];
[pool release];
return 0;
}
La sortida d'aquest exemple serà el següent: [Session started at 2005-09-01 17:23:47 +0200.] 2005-09-01 17:23:48.181 AppTargetaAdresa[2020] Contador: 1 2005-09-01 17:23:48.183 AppTargetaAdresa[2020] Tom Jones Aquest exemple mostra no només el que fa el mètode Normalment no es poden inicialitzar variables amb strings C perquè no suporten l'unicode. El següent exemple amb l'NSAutoreleasePool mostra un forma adient per fer strings i inicialitzar-los. Això és una forma de mantenir un variable membre en la gestió de la memòria. Una forma de mantenir aquest és crear copies dins dels teus mètodes. Autorelease PoolQuan vols començar a programar utilitzant NSString i altres classe de la framework Foundation necessites un sistema més flexible. Aquest sistema és utilitzant pous autoalliberables. Quan desenvolupem aplicacions Cocoa per a Mac, el pou auto alliberable s'activa automà ticament per tu. Mirem-ho en el següent exemple: AppPool.m
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pou = [[NSAutoreleasePool alloc] init];
// Creem les dades
NSString *str1 = @"string constant";
NSString *str2 = [NSString stringWithString: @"string gestionat perl pou"];
NSString *str3 = [[NSString alloc] initWithString: @"string auto gestionat"];
// Imprimim els contadors dels strings
NSLog(@"Contador: %@ = %d\n", str1, [str1 retainCount]);
NSLog(@"Contador: %@ = %d\n", str2, [str2 retainCount]);
NSLog(@"Contador: %@ = %d\n", str3, [str3 retainCount]);
// Alliberem la memòria
[str3 release];
[pou release];
return 0;
}
La sortida d'aquest exemple és: [Session started at 2005-09-01 17:43:03 +0200.] 2005-09-01 17:43:03.742 AppPool[2046] Contador: string constant = -1 2005-09-01 17:43:03.743 AppPool[2046] Contador: string gestionat perl pou = 1 2005-09-01 17:43:03.743 AppPool[2046] Contador: string auto gestionat = 1 Executable “AppPool� has exited with status 0. Veient la sortida del programa veiem algunes coses. Una d'aquestes és el comptador de l'str1 que és -1. L'altre és, que només s'allibera str3, i tot el programa gestiona perfectament la memòria. La raó és que la primera constant string és afegit a l'autoreleasepool automà ticament. Les altres strings es fa utilitzant stringWithString. Aquest mètode crea un string que es creat per la classe NSString, que també la posa en l'autorelease pool. És important recordar, per una gestió de memòria correcta, que els mètodes com Hi ha dues formes per gestinar la memòria a l'Objective-C: 1) retain i release o 2) retain i release / autorelease. Per cada retain, hi ha d'haver un release O un autorelease. El següent exemple mostra el que vull dir: Fraccio.h ... +(Fraccio*) fraccioAmbNumerador: (int) n iDenominador: (int) d; ... Faccio.m
...
+(Fraccio*) fraccioAmbNumerador: (int) n iDenominador: (int) d {
Fraccio *ret = [[Fraccio alloc] initAmbNumerador: n iDenominador: d];
[ret autorelease];
return ret;
}
...
AppAutorelease.m
#import <Foundation/Foundation.h>
#import "Fraccio.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Creem una instà ncia nova
Fraccio *frac1 = [Fraccio fraccioAmbNumerador: 2 iDenominador: 5];
Fraccio *frac2 = [Fraccio fraccioAmbNumerador: 1 iDenominador: 3];
// Ho imprimim
NSLog(@"La fracció 1 és: %@\n", [frac1 print]);
NSLog(@"La fracció 2 és: %@\n", [frac2 print]);
// Això causa un error de segmentació
//[frac1 release];
[pool release];
return 0;
}
El resultat de l'aplicació és el següent: [Session started at 2005-09-01 18:01:48 +0200.] 2005-09-01 18:01:48.137 AppAutoRelease[2091] La fracció 1 és: 2/5 2005-09-01 18:01:48.141 AppAutoRelease[2091] La fracció 2 és: 1/3 2005-09-01 18:01:48.141 AppAutoRelease[2091] Esborrant una fraccio 2005-09-01 18:01:48.141 AppAutoRelease[2091] Esborrant una fraccio Executable “AppAutoReleaseâ€? has exited with status 0. En aquest exempl, el mètode és un mètode de nivell de classe. Després que es crea l'objecte, s'auto-crida a l'autorelease. Dins del cos del mètode principal, no hem cridat cap Com diuen els comentaris, si descomentem la lÃnia causarem un error de segment. A partir de que s'ha cridat a la funció autorelease de l'objecte, i després en cridem un release, quan fem un release de l'autorelease pool intentarem cridar a la funció Les autoreleasepools poden crear-se dinà micament per una gran quantitat d'objectes temporals. Tots els objectes que han estat creats en un pool, després s'alliberen pel mateix pool. Com pots entendre, això vol dir que és impossible tenir més d'un autoreleas pool a l'hora. Algunes Classes de la Framework FoundationLa framework Foundation és similar a la Llibreria de Plantilles Està ndards del C++. Com que l'Objective-C té definició dinà mica real, les horribles plantilles del C++ ja no són necessà ries. Aquesta framework conté col·leccions, xarxes, fils i molts altres. NSArrayHi ha dos tipus d'arrays (i normalment de la majoria de classes de Foundation orientades a dades), l'NSArray i l'NSMutableArray. Com suggereix el nom, la Mutable pot canviar, i la NSArray no. Això vol dir que pot crear-se un NSArray però un cop el tens no pots canviar-ne la seva longitud. Anem a mirar un exemple: AppArray.m
#import <Foundation/Foundation.h>
void print( NSArray *array ) {
NSEnumerator *enumerator = [array objectEnumerator];
id obj;
while ( obj = [enumerator nextObject] ) {
NSLog( @"%@\n", [obj description] );
}
}
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray *arr = [[NSArray alloc] initWithObjects:
@"Jo", @"JoMateix", @"Io", nil];
NSMutableArray *mutable = [[NSMutableArray alloc] init];
// enumerate over items
NSLog( @"----static array\n" );
print( arr );
// add stuff
[mutable addObject: @"Un"];
[mutable addObject: @"Dos"];
[mutable addObjectsFromArray: arr];
[mutable addObject: @"Tres"];
// print em
NSLog( @"----mutable array\n" );
print( mutable );
// sort then print
NSLog( @"----sorted mutable array\n" );
[mutable sortUsingSelector: @selector( caseInsensitiveCompare: )];
print( mutable );
// free memory
[arr release];
[mutable release];
[pool release];
return 0;
}
El resultat de l'executable serà : [Session started at 2005-09-01 18:40:54 +0200.] 2005-09-01 18:40:54.600 AppArray[2168] ----static array 2005-09-01 18:40:54.611 AppArray[2168] Jo 2005-09-01 18:40:54.611 AppArray[2168] JoMateix 2005-09-01 18:40:54.612 AppArray[2168] Io 2005-09-01 18:40:54.612 AppArray[2168] ----mutable array 2005-09-01 18:40:54.613 AppArray[2168] Un 2005-09-01 18:40:54.613 AppArray[2168] Dos 2005-09-01 18:40:54.613 AppArray[2168] Jo 2005-09-01 18:40:54.613 AppArray[2168] JoMateix 2005-09-01 18:40:54.613 AppArray[2168] Io 2005-09-01 18:40:54.613 AppArray[2168] Tres 2005-09-01 18:40:54.613 AppArray[2168] ----sorted mutable array 2005-09-01 18:40:54.614 AppArray[2168] Dos 2005-09-01 18:40:54.614 AppArray[2168] Io 2005-09-01 18:40:54.614 AppArray[2168] Jo 2005-09-01 18:40:54.614 AppArray[2168] JoMateix 2005-09-01 18:40:54.614 AppArray[2168] Tres 2005-09-01 18:40:54.614 AppArray[2168] Un Executable “AppArray� has exited with status 0. En l'exemple inicialitzes un array mitjançant el constructor utilitzant Obj, Obj, Obj, ..., nil. El nil és un delimitador de final. L'ordenació mostra com ordenar un objecte utilitzant un selector. El selector indica a l'array qui decideix l'ordre. Si el teu objecte té diversos mètodes d'ordenació, pots escollir-ne qualsevol que vulguis utilitzant el seu selector. En el mètode print, s'ha usat el mètode descripció. Aquest és semblant al toString de Java. Retorna una representació NSString d'un objecte. L'NSEnumerator és semblant al sistema enumerador del Java. La raó perquè el NSDictionaryTambé mirarem per sobre com funciona l'NSDictionary amb el següent exemple: AppDictionary.m
#import <Foundation/Foundation.h>
void print( NSDictionary *map ) {
NSEnumerator *enumerator = [map keyEnumerator];
id key;
while ( key = [enumerator nextObject] ) {
NSLog( @"%@ => %@\n",
[key description],
[[map objectForKey: key] description] );
}
}
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
@"un", [NSNumber numberWithInt: 1],
@"dos", [NSNumber numberWithInt: 2],
@"tres", [NSNumber numberWithInt: 3],
nil];
NSMutableDictionary *mutable = [[NSMutableDictionary alloc] init];
// print dictionary
NSLog( @"----static dictionary\n" );
print( dictionary );
// add objects
[mutable setObject: @"Tom" forKey: @"tom@jones.com"];
[mutable setObject: @"Bob" forKey: @"bob@dole.com" ];
// print mutable dictionary
NSLog( @"----mutable dictionary\n" );
print( mutable );
// free memory
[dictionary release];
[mutable release];
[pool release];
return 0;
}
El resultat de l'executable serà : [Session started at 2005-09-01 18:56:23 +0200.] 2005-09-01 18:56:23.651 AppDictionary[2216] ----static dictionary 2005-09-01 18:56:23.660 AppDictionary[2216] 1 => un 2005-09-01 18:56:23.661 AppDictionary[2216] 2 => dos 2005-09-01 18:56:23.661 AppDictionary[2216] 3 => tres 2005-09-01 18:56:23.661 AppDictionary[2216] ----mutable dictionary 2005-09-01 18:56:23.662 AppDictionary[2216] bob@dole.com => Bob 2005-09-01 18:56:23.662 AppDictionary[2216] tom@jones.com => Tom Executable “AppDictionary� has exited with status 0. per Carles el 20/08/2005 - 16:02, actualitzat el 29/09/2005 - 16:55 | versió per a imprimir | entreu o registreu-vos per a enviar comentaris
|
apadrinamentsAjuda a fer crèixer la barra verda amb les teves donacions!
8% (20,0 de 256,0€) 61% (190,0 de 314,0€) 88% (1.011,0 de 1.144,5€) 0% (0,0 de 374,1€) 4% (50,0 de 1.235,0€) 47% (545,0 de 1.170,0€) 61% (700,0 de 1.155,0€) comesfa.orgel teu usuariopcionsQui està en lÃniaAra hi han 0 usuaris i 447 convidats connectats.
és populard'avui... |