• Català
  • Castellano
  • English


Introspecció


L'introspecció és una característica potent dels llenguatges i entorns orientats a objectes, i l'Objective-C i Cocoa són particularment generosos amb aquesta característica. La introspecció es refereix a la capacitat dels objectes per divulgar detalls sobre ells mateixos com objectes de l'execució. Aquests detalls inclouen la situació de l'objecte en l'arbre de la jerarquia, si conforma un protocol específic, i si respon a un cert missatge. El protocol i la classe NSObject defineix molts mètodes d'introspecció que pots utilitzar per consultar al sistema d'execució sobre la caracterització d'objectes.

Utilitzat amb judici, la introspecció fa a un programa orientat a objectes més eficient i robust. Pot ajudar-te a evitar error de despatx de missatges, assumpcions errònies d'igualtat d'objectes i problemes similiars. Les següents seccions mostren com pots utilitzar efectivament els mètodes d'introspecció d'NSObject en el teu codi.

Evaluant Relacions d'Herència

Un cop coneixes la classe d'un objecte, probablement coneixes una mica l'objecte. Pots conèixer quines són les seves capacitats, quins atributs representa i quins tipus de missatges poden respondre-hi. Fins i tot si després de la introspecció estàs desfamiliaritzar amb la classe en la qual prové l'objecte, ara coneixes prou per no enviar-hi certs missatges.

El protocol NSObject declara varis mètodes per determinar la posició d'un objecte en la jerarquia de classes. Aquests mètodes operen en diferents granularitats. Els mètodes d'instància de la class i la superclass, per exemple, retorna els objectes Classe representant la classe i superclasse, respectivament, del receptor. Aquests mètodes et requereixen que comparis una objecte Classe amb una altre. El Llistat 1 dona una exemple simple (algú diria trivial) del seu ús.

Llistat 1: Utilitzant els mètodes classe i superclasse

// ...
while ( id unObjecte = [objecteEnumerador nextObject] ) {
    if ( [self class] == [unObjecte superclass] ) {
        // fer quelcom apropia...
    }
}

Nota: Alguns cops utilitzes els mètodes class i superclass per obtenir un receptor apropiat per un missatge de classe.

Més comú és comprovar una afiliació de la classe d'un objecte, enviant un missatge isKindOfClass: o isMemberOfClass:. El mètode format retorna si el receptor és una instància d'una classe donada o una instància d'alguna classe que hereta de la classe. Un missatge isMemberOfClass:, per altra banda, t'explica si el receptor és una instància de la classe específica. El mètode isKingOfClass: és generalment més útil perquè pots conèixer en un moment el rang complet de missatges que pots enviar a un objecte. Considereu el petit codi del Llistat 2.

Llistat 2: Utilitzant isKindOfClass:

if ([item isKindOfClass:[NSData class]]) {
    const unsigned char *bytes = [item bytes];
    unsigned int longitud = [item length];
    // ...
}

Per aprendre que l'objecte item hereta de la classe NSData, aquest codi coneix si pot enviar-li els missatges de NSData bytes i length. La diferència entre isKindOfClass: i isMemberOfClass: sembla aparent si assumeixes que item és una instància de NSMutableData. Si utilitzes isMemberOfClass: en comptes de isKindOfClass:, el bloc de codi condicionat no s'executarà mai perquè item no és una instància de NSData però si de NSMutableData, una sub-classe de NSData.

Implementació de Mètodes i Conformitat de Protocol

Dos dels mètodes d'introspecció més potents de NSObject, són respondsToSelector: i conformsToProtocol:. Aquests mètodes et diuen, respectivament, si un objecte implementa un cert mètode i si un objecte conforma un protocol formal específic (és a dir, adopta el protocol, si és necessari i implementa tots els mètodes del protocol).

Utilitza aquests mètodes en una situació similar en aquest code. Ells et permeten per descobrir si algun objecte potencialment anònim respon apropiadament a un missatge en particular o a un conjunt de missatges abans que li enviïs qualsevol d'aquests missatges. Per fer aquesta comprovació abans d'enviar un missatge, pots evitar el risc de les excepcions durant l'execució a causa dels selectors no reconeguts. L'Aplication Kit implementa els protocols informals -- la base de la delegació -- per comprovar si el delegat implementa un mètode delegat (utilitzant respondsToSelector:) abans d'invocar aquest mètode.

El Llistat 3 il·lustra com pots utilitzar el mètode respondsToSelector: en el teu codi:

Llistat 3: Utilitzant respondsToSelector:

- (void)ferComandaPerSelector: (SEL) unSelector {
    if ([self respondsToSelector: unSelector]) {
        [self performSelector: unSelector withObject: nil];
    } else {
        [client ferComandaPerSelector: unSelector];
    }
}

El Llistat 4 il·lustra com hauries d'utilitzar el mètode conformsToProtocol: en el teu codi:

Llista 4: Utilitzant conformsToProtocol:

// ...
if (!([((id)objecteTest) conformsToProtocol: @protocol(NSMenuItem)])) {
    NSLog(@"MenuItem Personalitzat, '%@', no carregat; ha de conformar al
           protocol 'NSMenuItem'.\n", [objecteTest class]);
    [objecteTest release];
    objecteTest = nil;
}

Comparació d'Objectes

Encara que no són estrictament mètodes d'introspecció, els mètodes hash i isEqual: segueixen un rol semblant. Hi ha eines d'execució indispensables per identificar i comparar objecte. Però més enllà de la consulta durant l'execució de la informació sobre l'objecte, realitzen una comparació lògica específica de la classe.

Els mètodes hash i isEqual:, declarats mitjançant el protocol NSObject,
estan molt relacionats. El mètode hash ha d'implementar-se per retornar un enter que pot utiltizar-se com una adreça de taula en una clau d'estructura de taula. Si dos objecte són iguals (es determina pel mètode isEqual:), tindran el mateix valor hash. Si els teu objecte podria ser inclòs en una col·lecció com NSSet, necessites definit hash i verificar l'invariant que si dos objectes són iguals, retornen el mateix valor hash. La implementació per defecte del mètode isEqual: de l'NSObject simplement comprova la igualtat del punter.

Utilitzar el mètode isEqual: és directe; compara el receptor amb l'objecte adjuntat com argument. La comparació d'objectes freqüentment informa al sistema d'execució les decisions sobre que s'hauria de fer amb un objecte. Com mostra el Llistat 5, pots utilitzar isEqual: per decidir si executar una acció, en aquest cas per desar les preferències d'usuari, si s'han modificat.

Llistat 5: Utilitzant isEqual:

- (void) desaDefecte {
    NSDictionary *prefs =[self preferencies];
    if ( ![valorsOriginals isEqual: prefs] ) 
        [Preferences savePreferencesToDefaults: prefs]];
}

Si estàs creant una sub-classe, pots necessitar sobre-escriure isEqual: per afegir més enllà de comprovar l'igualtat dels punters. La sub-classe podria definir un atribut extra que podria haver estat el mateix valor en dos instàncies per considerar-se iguals. Per exemple, diguem que crees una sub-classe de NSObject anomenada ElMeuWidget que conté dos variables d'instància, nom i dades. Ambdues han de tenir el mateix valor per dues instàncies de ElMeuWidget per considerar-se iguals. El Llistat 6 il·lustra com pots implementar el isEqual: per a classe ElMeuWidget.

Llistat 6: Sobre-escriure isEqual:

-(BOOL) isEqual: (id) unaAltra {
    if (unaAltra == self)
        return YES;
    if (!unaAltra || ![unaAltra isKindOfClass: [self class]])
        return NO;
    return [self isEqualAlWidget: unaAltra];
}

- (BOOL) isEqualAlWitget: (ElMeuWidget*) unWidget {
    if (self==unWidget)
        return YES;
    if (![(id)[self nom] isEqual: [unWidget nom]])
        return NO;
    if (![[self dades] isEqualALesDades: [unWidget dades]])
        return NO;
    return YES;
}

El mètode isEqual: primer comprova per l'igualtat del punter, després que siguin classes iguals, i finalment invoca al comparador de l'objecte el qual el nom indica la classe de l'objecte involucrat en la comparació. Aquest tipus de comparador, el qual força la comprovació de tipus de l'objecte passa, és una convenció comuna dins Cocoa; el mètodes isEqualToString: de NSString i isEqualToTimeZone: de NSTimeZone en són dos exemples. El comparador específic de classe -- en aquest cas isEqualToTimeZone -- executa la comprovació d'igualtat del nom i les dades.

En tots els mètodes isEqualToType: de les frameworks Cocoa, nil no és un argument vàlid i les implementacions d'aquests mètodes poden caure al rebre un nil. No obstant, per compatibilitat enrera, els mètode isEqual: de les frameworks Cocoa accepten nil, retornant NO.