2009-05-31 12 views
10

Tengo una serie de objetos de "política" que pensé que sería conveniente implementar como métodos de clase en un conjunto de clases de políticas. He especificado un protocolo para esto, y creado clases para ajustarse a (sólo una se muestra a continuación)¿Cómo llamo a los métodos de + clase en Objective C sin hacer referencia a la clase?

@protocol Counter 
+(NSInteger) countFor: (Model *)model; 
@end 

@interface CurrentListCounter : NSObject <Counter> 
+(NSInteger) countFor: (Model *)model; 
@end 

entonces tengo una matriz de las clases que se ajustan a este protocolo (como CurrentListCounter hace)

+(NSArray *) availableCounters { 
return [[[NSArray alloc] initWithObjects: [CurrentListCounter class], [AllListsCounter class], nil] autorelease]; 
} 

Observe cómo estoy usando las clases como objetos (y este podría ser mi problema - en clases Smalltalk son objetos como todo lo demás - No estoy seguro de si están en Objective-C?)

Mi problema es exactamente cuando quiero llamar al método cuando tomo uno de los po lítica objetos fuera de la matriz:

id<Counter> counter = [[MyModel availableCounters] objectAtIndex: self.index]; 
return [counter countFor: self]; 

recibo una advertencia en la instrucción de retorno - dice -countFor: no se ha encontrado en el protocolo (por lo que su asumiendo su un método de instancia donde quiero llamar a un método de clase). Sin embargo, como los objetos en mi matriz son instancias de clase, ahora son como los métodos de instancia (o conceptualmente deberían serlo).

¿Existe alguna forma mágica de llamar a los métodos de clase? ¿O es solo una mala idea y debería simplemente crear instancias de mis objetos de política (y no usar métodos de clase)?

+0

Contador no contador –

Respuesta

19

Este

id <Counter> counter = [[Model availableCounters] objectAtIndex:0]; 
return ([counter countFor: nil]); 

Debe ser

Class <Counter> counter = [[Model availableCounters] objectAtIndex:0]; 
return ([counter countFor: nil]); 

En el primero hay una instancia que se ajusta a <Counter>. En el segundo, tienes una clase que cumple con <Counter>. La advertencia del compilador es correcta porque las instancias que cumplen con <Counter> no responden a countFor:, solo las clases lo hacen.

+0

También estoy de acuerdo en que se siente mejor usar instancias que clases aquí. –

+0

Esta respuesta es correcta: al principio no noté la sutileza de la clase frente a la identificación , que en realidad responde al título original de mi publicación. – TimM

0

Resulta que realmente está funcionando y la advertencia es incorrecta.

Entonces, la pregunta sigue siendo si es razonable hacer algo (¿usar métodos de clase si no necesitan ningún estado)?

¿Y cómo manejar mejor la advertencia (me gusta correr sin advertencia)?

Mi única solución era tener un segundo protocolo (esencialmente el mismo que el primero, pero declarar que en el lado de ejemplo):

@protocol CounterInstance 
-(NSInteger) countFor: (Model *)model; 
@end 

Y dónde puedo acceder a los contadores de utilizarla en su lugar (para engañar al compilador):

id<CounterInstance> counter = [[MyModel availableCounters] objectAtIndex: self.index]; 
return [counter countFor: self]; 

Es un poco incómodo pero funciona. ¿Me interesan las opiniones de los demás?

+0

La advertencia no es incorrecta. La naturaleza dinámica de Objective-C significa que el mensaje se envía al objeto de todos modos. El compilador está planteando la advertencia para hacerle saber que realmente está esperando un método de instancia. En cuanto a su solución, creo que es fea. Solo haz lo que dijo Jim. – Abizern

5

Una clase en Objective-C funciona como una instancia; la principal diferencia subyacente es que retener el recuento no hace nada con ellos. Sin embargo, lo que está almacenando en la matriz -availableCounters no son instancias (el tipo id) sino clases, que tienen un tipo de Class. Por lo tanto, con la definición -availableCounters ha especificado anteriormente, lo que necesita es la siguiente:

Class counterClass = [[MyModel availableCounters] objectAtIndex: self.index]; 
return ([counterClass countFor: self]); 

Sin embargo, probablemente sería semánticamente mejor si usted utiliza casos en lugar de clases.En ese caso, se podría hacer algo como lo siguiente:

@protocol Counter 
- (NSUInteger) countFor: (Model *) model; 
@end 

@interface CurrentListCounter : NSObject<Counter> 
@end 

@interface SomeOtherCounter : NSObject<Counter> 
@end 

Entonces la clase del modelo podrían ejecutar las siguientes:

static NSArray * globalCounterList = nil; 

+ (void) initialize 
{ 
    if (self != [Model class]) 
     return; 

    globalCounterList = [[NSArray alloc] initWithObjects: [[[CurrentListCounter alloc] init] autorelease], [[[SomeOtherCounter alloc] init] autorelease], nil]; 
} 

+ (NSArray *) availableCounters 
{ 
    return (globalCounterList); 
} 

A continuación, el uso de eso sería exactamente como se ha especificado anteriormente:

id<Counter> counter = [[Model availableCounters] objectAtIndex: self.index]; 
return ([counter countFor: self]); 
+0

Gracias Jim - Estaba interesado en ver qué tan lejos puedes empujar Objective-C. Parece que cuando llegas a las clases es cuando comienza a actuar de forma un poco diferente (aunque funciona, las clases siguen siendo objetos, lo cual es bueno), así que quizás sea mejor seguir las instancias. Una cosa en el comienzo del comentario - "Class counterClass" es evitar el protocolo, mientras que mis clases también se ajustan a "Counter". Supongo que lo que falta es poder especificar adecuadamente los protocolos para las clases (en el lado de la clase) - algo como: @interface CurrentListCounter: NSObject <+Counter> – TimM

+1

Es posible que pueda utilizar la clase counterClass. No estoy seguro. Pero hacerlo solo realmente limita los mensajes que el compilador no advertirá: la comprobación de tipo ObjC es realmente solo de asesoramiento. Y los protocolos pueden especificar métodos de clase, por lo que no necesitaría ningún tipo de sintaxis especial <+Counter> (consulte el protocolo NSObject, por ejemplo). –

+0

Frío: esa es la solución: Clase contador = [[Contadores disponibles del modelo] .... Esto le dice al compilador que tengo una instancia de una clase que se ajusta a ese protocolo. Por supuesto, sigue siendo una cuestión de gusto sobre si el uso de clases como objetos es deseable en Obj-c (el consenso parece no) – TimM

Cuestiones relacionadas