• Català
  • Castellano
  • English


Objectes Classe


La definició d'una clase conté tipus d'informació variada, molta sobre les instàncies de la clase:

  • El nom de la classe i la seva superclasse.
  • Una plantilla describint el conjunt de les variables d'instància
  • Les declaracions dels noms dels mètodes i els seus tipus d'arguments i retorn
  • Les implementacions dels mètodes

Aquesta informació és compilada i guardada en estructures de dades disponibles pel sistema d'execució. El compilador crea només un objecte, un objecte classe, que representa la classe. L'objecte classe té accés a tota la informació sobre la classe, que principalment és la informació sobre quines instàncies de la classe són correctes. És possible produir noves instàncies d'acord amb el plà proposat en la definició de la classe.

Encara que un objecte de classe manté el prototip de la instància de la classe, no és una instància. No té variables d'instància pròpies i no pot executar mètodes previstos per les instàncies de la classe. Tanmateix, una definició de classe pot incloure mètodes previstos especialment per l'objecte classe -- els mètodes de classe en contraposició a els els mètodes d'instància. Un objecte classe hereda els mètodes de classe de les classes sobre seu en la jerarquia, així com les instàncies hereden els mètodes d'instància.

En el codi font, l'objecte classe es representa pel nom de la classe. En el següent exemple, la classe Rectangle retorna el número de versió de la classe utilitzant un mètode heredat de la classe NSObject:

int numeroDeVersio = [Rectangle versio];

Tanmateix, el nom de classe només significa per l'objecte classe qui és el receptor en una expressió de missatge. Per altra banda, necessites preguntar a una intància o a la classe per retornar la classe id. Ambdos responen al missatge class:

id unaClassw = [unObjecte class];
id classeRect = [Rectangle class];

Com mostren aquests exemple, els objectes classe poden, com tots els altres objectes, definirse com id. Però els objectes classe també poden ser definits més específicament a un tipus de dada Clas:

Class unaClass = [unObjecte class];
Class classeRect = [Rectangle class];

Tots els objectes classe són del tipus Class. Utilitzant aquest nom de tipus per una classe és equivalent a utilitzar el nom classe per definir estàticament una instància.

Els objectes classe són objectes completament definits que poden definir-se dinàmicament, rebre missatges, i heredar mètodes d'altres classes. Només són especials en que estan creats pel compilador, la carència d'estructures de dades (variables d'instància) pròpies a part de les construides per la definició de la classe, i que són els encarregats de produir els instàncies en temps d'execució.

Nota: El compilador també construeix un "objecte metaclasse" per cada classe. Que descriu l'objecte classe tal com l'objecte classe descriu les instàncies de la classe. Però mentre pots enviar missatges a les instàncies i a l'objecte classe, l'objecte metaclasse només s'utilitza internament pel sistema d'execució.

Creant Instàncies

La principal funció de un objecte classe és crear noves instàncies. Aquest codi explica com la classe Rectangle crea una nova instància Rectangle i l'assigna a la variables elMeuRect:

id elMeuRect;
elMeuRect = [Rectangle alloc];

El mètode alloc assigna dinàmicament memòria per a les noves variables d'instància de l'objecte i les inicialitza totes a 0 -- totes, excepte la variable isa que connecta la nova instància a la seva classe. Per que un objecte sigui útil, aquest normalment necessita ser inicialitzat més a fons. Això és funció d'un mètode init. La inicialització normalment és immediatament a continuació després de l'assignació:

elMeuRect = [[Rectangle alloc] init];

Aquesta linia de codi, o una semblant, podria ser necessaria abans que elMeuRect pugui rebre qualsevol dels missatges que s'han mostrat en exemples previs en aquest capítol. El mètode alloc retorna una nova instància i aquesta instància executa un mètode init per activar el seu estat inicial. Cada objecte classe té almenys un mètode (com alloc) que permet que es produeixin nous objectes, i cada instància té almenys un mètode (com init) que el prepara pel seu ús. Els mètode d'inicialització sovin prenen arguments per permetre que valors particulars siguin passats i tenen paraules clau per etiquetar els arguments (initWithPosition:size:, per exemple, és un mètode que podria inicialitzar una nova instància de Rectangle), però tots comencen amb "init".

Personalització Amb Objectes Classe

No és només un caprici del llenguatge Objective-C que les classes siguin tractades com objectes. És una opció que té beneficis intecionats, i de vegades sorprenents, pel disseny. És possible, per exemple, personalitzar un objecte amb una classe, on la classe pertany a un conjunt ilimitat. En el Kit Application, per exemple, un objecte NSMatrix pot personalitzar-se amb un tipus particular de NSCell.

Una classe NSMatrix pot agafar responsabilitats per crear els objectes individuals que representen les seves cel·les. Pot fer això, quan la classe NSMatrix és inicialitzada primer i després quan es necessitin noves cel·les. La matriu visible que un objecte NSMatrix dibuixa en una pantalla pot creixer i reduir-se en temps d'execució, potser en resposta a accions d'usuari. Quan creix, la NSMatrix necessita ser capaç de produir nous objectes per omplir els nous espais que s'afegeixen.

Però quin tipus d'objectes podrien ser? Cada NSMatrix mostra només un tipus de NSCell, però hi ha molts tipus diferents. La jerarquia d'herència en la Figura 2-3 mostra algunes d'aquelles proporcionades pel Kit Application. Totes heredades de la classe genèrica NSCell:

Figure 2-3 Jerarquia d'herència per NSCells

Figure 2-3 Jerarquia d'herència per NSCells

Quan un NSMatrix crea objectes NSCell, podrien ser NSButtonCells per mostrar un banc de botons o interruptors, NSTextFieldCells per mostrar texts on l'usuari pot entrar i editar texte, o algun altre tipus de NSCell? El MSMatrix ha de permetre per qualsevol tipus de NSCell, fins hi tot els que encara no s'han creat.

Una solució per aquest problema és definir una classe NSMatrix com una classe abstracte i requerir a cadascú que la utilitzi que declari una subclasse i implementi els mètodes que produeixin noves cel·les. Com que podrien implementar-se els mètodes, els usuaris de la classe podrien assegurar-se que els objectes que haguesin crear fosin del tipus correcte.

Però això requereix que altres facin el treball que hauria de fer la classe NSMatrix, i això proliferaria el nombre de classes innecessariament. Ja que una aplicació pot necessitar més d'un tipus de NSMatrix, cadascun amb un tipus diferents de NSCell, això podria arribar a omplir-se amb subclasses NSMatrix. Cada vegada que creesis un nou tipus de NSCell, també hauries de definir un nou tipus de NSMatrix. A més, els programadors en diferents projectes podrien estar escribint codi virtualment idèntic per fer la mateixa feina, tot el muntat sobre NSMatrix és incorrecte de fer.

La millor solució, la solució que la classe NSMatrix adopta actualment, és permetre a les instàncies NSMatrix ser inicialitzades amb un tipus de NSCell -- amb un objecte classe. Aquest definexi un mètode setCellClass: que passa l'objecte classe del tipus d'objecte NSCell i l'NSMatrix hauria d'utilitzar per omplir els espais buits:

[laMevaMatriu setCellClass:[NSButtonCell class]];

La classe NSMatrix utilitza l'objecte classe per produir noves cel·les quan aquest és inicialitzat per primer cop i si aquest canvia de mida per contindre més cel·les. Aquest tipus de personalització podria ser difícil si les classes no fosin objecte que podrien passar-se en missatges i assignar-se a variables.

Variables i Objectes Classe

Quan defineixes una nova classe d'objectes, pots especificar les variables d'instància per ells. Cada instància de la calsse tindrà la seva pròpia copia de totes les variables que declaris; cada objecte control la seva propia informació.

Tanmateix, no pots descriure variables per l'objecte classe; no hi han "variables de classe" en contraposició a les variables d'instància. Només les estructures de dades internes, inicialitzades en la definició de la classe, es proporcionen a la classe. L'objecte classe tampoc té access a les variables d'instància de cap instància; aquest no pot inicialitzar, llegir, o alterar-les.

Encara que, per que totes les instàncies d'una classe comparteixin dades, es requereiz una variable externa d'algun tipus. Algunes classes declaren variables estàtiques i proporcionen mètodes per gestionar-los. (Declarant una variable static en el mateix fitxer on la definició de la clase limita el seu abast justament a la classe -- i justament la part de la classe que està implementada en el fitxer. A diferència de les variables d'instància, les variables estàtiques no pode heredar-se per les subclasses).

Les variables estàtiques ajuden a donar als objectes de classe més funcionalitat que la que de "fàbrica" ofereixen les instàncies; poden arribar a ser pròpiament un objecte complert i versàtil. Un objecte de classe pot utilitzar-se per coordinar les instàncies que crea, distribuir instàncies en llistes d'objectes ja creats, o gestionar altres processos esencials de l'aplicació. En el cas que només necessitis un objecte d'una classe en particular, pots possar tots els estat dels objectes dins de les variables estàtiques i només utilitzar els mètodes de clase. Això estalvia el pas d'assignar i inicialitzar una instància.

Nota: També és possible utilitzar variables externes que no estiquin declarades static, però l'abast limitat de les variables estàtiques serveixen per proporcionar l'encapsulació de dades en objectes separats.

Inicialitzant un Objecte Classe

Si un objecte de classe s'utilitza per res més que per assignar instàncies, podria inicialtizar-se com un ho fa una intància. Encara que els programes no assignin un objecte a la classe, l'Objective-C proporciona una forma als programes per inicialitzar-los.

Si una classe utilitza variables estàtiques o globals, el mètode initialize és un bon lloc on indicar els seus valors inicials. Per exemple, si una classe manté una matriu de instàncies, el mètode initialize

El sistema d'execució envia un missatge initialize a cada objecte de classe abans que la classe rebi qualsevol altre missatge i després que la seva super-classe hagi rebut el missatge initialize. Això dona a la classe un posibilitat per activar el seu entorn d'execució abans que sigui utiltizat. Si la inicialització no es requereix, no necessites re-escriure el mètode initialize que respongui al missatge; la classe NSObject en defineix una versió buida que la teva classe hereda.

A causa de l'herència, un missatge initialize enviat a una classe que no té implementat el mètode initialize és re-enviat a la super-classe, encara que ja hagi rebut el missatge initialize. Per exemple, asumim que la classe A implementa el mètode initialize, i la classe B hereta de la classe A però no implementa el mètode initialize. Abans que la classe B rebi el primer missatge el sistema d'execució li envia un initialize. Però, com que la classe B no implementa l'initialize, l'initialize de la classe A s'executa el lloc d'ell. Tanmateix, la classe A ha d'estar segura que la seva inicialització lògica només s'ha executat un cop.

Per evitar l'execució de la inicialització lògica més d'un cop, utilitzeu la plantilla de l'exemple següent quan implementeu el mètode initialize.

Llistat 2-1: Implementació del mètode initialize

+ (void)initialize
{
    static BOOL initialized = NO;
    if (!initialized) {
        // Executa la inicialització aquí.
        ...
        initialized = YES;
    }
}

Nota: Recordeu que el sistema d'execució envia un initialize a cada classe individualment. Per tant, en la implementació del mètode initialize d'una classe, no has d'enviar el missatge initialize a la seva super-classe.

Mètodes de la Classe Arrel

Tots els objectes, així com les classes i les instàncies, necessiten una interfície cap al sistema d'execució. Tant els objectes de classe i les instàncies han de ser capaces d'introspeccionar sobre les seves capacitats i informar la seva situació en la jerarquia d'herència. És la disposició de la classe NSObject per proporcionar aquesta interfície.

Així que els mètodes del NSObject no tenen que implementar-se dos cops -- un cop per proporcionar una interfície d'execució per les instàncies i una latre per duplicar aquesta interfície pels objectes de classe -- els objectes de clase donen una administració especial per executar els mètodes d'intància definits en la classe arrel. Quan un objecte de classe rep un missatge que aquest no pot respondre amb un mètode de clase, el sistema d'execució determina si hi ha una mètode d'intància arrel que pugui respondre. Els únics mètodes d'intància que un objecte de classe pot executar són aquells definits en la classe arrel, i només si no hi han mètodes de classe que poden fer aquesta feina.

Per més informació d'aquesta peculiar abilitat dels objectes de classe per executar mètodes d'instància arrel, mireu l'especificació de la classe NSObject en la referència del marc de treball Foundation.