2009-05-31 14 views
10

J'ai une série d'objets de "politique" que je pensais être pratique à implémenter en tant que méthodes de classe sur un ensemble de classes de politiques. J'ai spécifié un protocole pour cela, les classes créées pour se conformer à (juste un illustré ci-dessous)Comment appeler les méthodes + class dans Objective C sans faire référence à la classe?

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

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

J'ai alors un tableau des classes qui sont conformes à ce protocole (comme CurrentListCounter ne)

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

Remarquez comment j'utilise les classes comme des objets (et cela pourrait être mon problème - dans les classes Smalltalk sont des objets comme tout le reste - Je ne sais pas si elles sont en Objective-C?)

mon problème exact est quand je veux appeler la méthode quand je prends l'un des po objets LICY sur le tableau:

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

je reçois un avertissement sur la déclaration de retour - il dit -countFor: non trouvé dans le protocole (de sorte que son hypothèse d'une méthode d'instance où je veux appeler une méthode de classe). Cependant, comme les objets de mon tableau sont des instances de classe, ils sont maintenant comme des méthodes d'instance (ou conceptuellement ils devraient l'être).

est-il un moyen magique pour appeler des méthodes de classe? Ou est-ce juste une mauvaise idée et je devrais juste créer des instances de mes objets de politique (et ne pas utiliser les méthodes de classe)?

+0

Compteur sans compteur –

Répondre

19

Ce

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

devrait être

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

Dans le premier, vous avez une instance qui est conforme à <Counter>. Dans la seconde vous avez une classe qui est conforme à <Counter>. L'avertissement du compilateur est correct car les instances conformes à <Counter> ne répondent pas à countFor:, seules les classes le font.

+0

Je suis également d'accord qu'il est préférable d'utiliser des instances que des classes ici. –

+0

Cette réponse est correcte - au début, je n'ai pas remarqué la subtilité de la classe vs ID - qui répond réellement le titre original de mon message. – TimM

0

Il se trouve qu'il fonctionne réellement et l'avertissement est incorrect.

Ainsi, la question demeure de savoir si c'est une chose raisonnable à faire (utiliser des méthodes de classe si elles ne nécessitent pas d'état)?

Et comment gérer au mieux l'avertissement (j'aime courir avertissement gratuit)?

Ma seule solution était d'avoir un second protocole (essentiellement le même que le premier, mais le déclarer sur le côté par exemple):

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

Et où j'accéder aux compteurs utiliser à la place (pour tromper le compilateur):

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

C'est un peu bizarre, mais cela fonctionne. Suis intéressé par les opinions des autres?

+0

L'avertissement n'est pas incorrect. La nature dynamique d'Objective-C signifie que le message est quand même envoyé à l'objet. Le compilateur lève l'avertissement pour vous faire savoir qu'il attend vraiment une méthode d'instance. Quant à votre solution - je pense que c'est moche. Fais ce que Jim a dit. – Abizern

5

Une classe en Objective-C ne fonctionne comme une instance - la principale différence sous-jacente est que le comptage ne fait rien retenir sur eux. Toutefois, ce que vous stockez dans le tableau -availableCounters ne sont pas des instances (le type id) mais des classes, qui ont un type de Class. Par conséquent, la définition -availableCounters spécifié ci-dessus, ce dont vous avez besoin est le suivant:

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

Cependant, il serait probablement mieux sémantiquement si vous avez utilisé des cas plutôt que des classes.Dans ce cas, vous pouvez faire quelque chose comme ce qui suit:

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

@interface CurrentListCounter : NSObject<Counter> 
@end 

@interface SomeOtherCounter : NSObject<Counter> 
@end 

Ensuite, votre classe de modèle pourrait mettre en œuvre les suivantes:

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); 
} 

Ensuite, votre utilisation de ce serait exactement comme spécifié ci-dessus:

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

Merci Jim - J'étais intéressé de voir jusqu'où vous pouvez pousser Objective-C. Cela ressemble à quand vous frappez des classes c'est où il commence à agir un peu différemment (bien que cela fonctionne - les classes sont toujours des objets, ce qui est bien) - alors peut-être vaut-il mieux s'en tenir aux instances. Une chose au début du commentaire - "Class counterClass" évite le protocole, alors que mes classes sont également conformes à "Counter". Je suppose que ce qui manque est de pouvoir spécifier correctement les protocoles pour les classes (du côté de la classe) - quelque chose comme: @interface CurrentListCounter: NSObject <+Counter> – TimM

+1

Vous pourriez utiliser la classe counterClass. Je ne suis pas sûr. Mais cela ne limite vraiment que les messages que le compilateur ne mettra pas en garde - la vérification de type ObjC n'est vraiment que consultative. Et les protocoles peuvent spécifier des méthodes de classe, vous n'aurez donc pas besoin de syntaxe spéciale <+Counter> (regardez le protocole NSObject, par exemple). –

+0

Cool - c'est la solution: Classe counter = [[Modèles disponiblesCounters] .... Ceci indique au compilateur que j'ai une instance d'une classe conforme à ce protocole. Bien sûr, c'est toujours une question de goût quant à savoir si l'utilisation de classes en tant qu'objets est souhaitable dans Obj-c (le consensus semble non) – TimM