2010-01-22 7 views
2

Je suis novice dans le domaine de l'Objective C (développeur Java la plupart du temps) et je suis en train de me lancer dans ma première application de tueur. :-) Pour l'instant, je suis un peu confus à propos de l'utilisation des sélecteurs comme arguments de méthode. Ils semblent être un peu différents des délégués en C# par exemple.Signature de méthode pour un sélecteur

Compte tenu de la signature de la méthode suivante

-(void)execute:(SEL)callback; 

est-il un moyen de faire respecter la signature du sélecteur est passé à une telle méthode? La méthode attend un sélecteur d'une méthode avec la signature suivante

-(void)foo:(NSData*)data; 

Mais le SEL (type) est générique, donc il y a une bonne chance de passer un mauvais sélecteur sur la méthode exécuter. OK au moins au moment de l'exécution on verrait un comportement amusant ... mais je voudrais voir un avertissement/erreur du compilateur lorsque cela se produit.

+0

Je ne connais pas la réponse à cette question (s'il y en a une), mais je veux juste noter qu'il vaut la peine de vérifier que l'objet récepteur répond réellement au sélecteur: if ([obj répondsToSelector: ...]). De cette façon, vous savez au moins que le sélecteur passé est raisonnable pour l'objet en question. –

Répondre

6

La réponse rapide est: non, il n'y a pas moyen d'avoir le compilateur appliquer la signature de la méthode d'un sélecteur de méthode qui est fournie par un argument SEL. L'un des points forts d'Objective-C est qu'il s'agit d'un langage faiblement typé, qui permet un comportement beaucoup plus dynamique. Bien sûr, cela se fait au détriment de la sécurité de type à la compilation. Afin de faire ce que (je pense) que vous voulez, la meilleure approche est d'utiliser des délégués. Cocoa utilise des délégués pour permettre à une autre classe d'implémenter des méthodes de type "callback". Voici comment il pourrait ressembler:

FooController.h

@protocol FooControllerDelegate 
@required: 
- (void)handleData:(NSData *)data forFoo:(FooController *)foo; 
@end 

@interface FooController : NSObject 
{ 
    id <FooControllerDelegate> * delegate; 
} 
@property (assign) id <FooControllerDelegate> * delegate; 
- (void)doStuff; 
@end 

FooController.m

@interface FooController (delegateCalls) 
- (void)handleData:(NSData *)data; 
@end 

@implementation FooController 

@synthesize delegate; 

- (id)init 
{ 
    if ((self = [super init]) == nil) { return nil; } 
    delegate = nil; 
    ... 
    return self; 
} 

- (void)doStuff 
{ 
    ... 
    [self handleData:data]; 
} 

- (void)handleData:(NSData *)data 
{ 
    if (delegate != nil) 
    { 
     [delegate handleData:data forFoo:self]; 
    } 
    else 
    { 
     return; 
     // or throw an error 
     // or handle it yourself 
    } 
} 

@end 

Utilisation du @required mot-clé dans votre protocole de délégué vous empêchera d'affecter un délégué à un FooController qui n'implémente pas la méthode exactement comme décrit dans le protocole. Tenter de fournir un délégué qui ne correspond pas à la méthode de protocole @required entraînera une erreur de compilation.

Voici comment vous créer une classe de délégué pour travailler avec le code ci-dessus:

@interface MyFooHandler <FooControllerDelegate> : NSObject 
{ 
} 
- (void)handleData:(NSData *)data forFoo:(FooController *)foo; 
@end 

@implementation MyFooHandler 
- (void)handleData:(NSData *)data forFoo:(FooController *)foo 
{ 
    // do something here 
} 
@end 

Et voici comment vous utiliseriez tout:

FooController * foo = [[FooController alloc] init]; 
MyFooHandler * fooHandler = [[MyFooHandler alloc] init]; 
... 
[foo setDelegate:fooHandler]; // this would cause a compiler error if fooHandler 
           // did not implement the protocol properly 
... 
[foo doStuff]; // this will call the delegate method on fooHandler 
... 
[fooHandler release]; 
[foo release]; 
+0

Merci. Cela semble être la voie à suivre dans l'objectif c (j'utilise cette approche pour extraire les résultats des sous-vues). Est un modèle commun dans le cacao. Beaucoup de code pour simplement déléguer les résultats de quelques actions asynchrones à des méthodes spécifiques. Je n'aime pas changer de déclaration. Ils sont une capitulation face à la POO. ;) Quand il y a beaucoup d'actions à gérer, il faut avoir une méthode divine répondant à tous les objets action. – MacTouch

+0

Je partage votre sentiment à propos des déclarations de changement, mais je ne suis pas tout à fait sûr comment cela s'applique au développement Objective-C. J'ai écrit une application de bureau grande dans Cocoa, et réussi à éviter le genre de programmation non-OO que vous décrivez. Si vous avez besoin d'un grand nombre d'instructions switch ou de méthodes divines, il se peut que vous abordiez quelque chose sous un mauvais angle. –

+0

Oui, vous avez raison. Je repense maintenant ce que je fais avant que les choses ne deviennent compliquées. ;) J'ai une classe Action abstraite et quelques implémentations dérivées de celle-ci. Toutes les actions lisent des données à partir de différents endroits dans le «Web du monde sauvage» et mappent les résultats aux objets de données avec différents types (Message, Personne, etc.) Toutes les actions sont appelées dans le même contrôleur. Le contrôleur doit répondre à chaque action d'une manière différente. L'idée était de déléguer les résultats à différentes méthodes dans le contrôleur en utilisant un sélecteur comme argument de la méthode init de l'implémentation de l'action. – MacTouch

0

Si vous souhaitez appliquer le traitement des données, utilisez isKindOfClass dans votre sélecteur. Cela fonctionne beaucoup comme instanceof que vous connaissez en Java.

+0

Merci, mais cela ne s'applique qu'à l'exécution. Je voudrais éviter de telles "erreurs" dans le code et laisser le compilateur le signaler au moment de la compilation. Similaire aux protocoles. id Tout ce qui ne s'applique pas au protocole SomeProtocol entraîne des avertissements de compilateur. Il n'y a pas une telle notation pour les sélecteurs? SEL À droite? En Java, je voudrais spécifier une interface avec la signature de méthode de rappel (similaire à Runnable par exemple) et passer une implémentation anonyme à la méthode. En C# on peut spécifier la signature d'un délégué. Mais comment le faire dans l'objectif c? – MacTouch

+0

Malheureusement, toutes les bonnes habitudes (problèmes d'exécution de trading pour les problèmes de compilation) que nous avons apprises en Java sont pratiquement ignorées dans Objective-C. C'est un système dynamique. Les protocoles peuvent aider à vous donner le polymorphisme que vous voulez, mais ne vous aident toujours pas avec la sécurité de type sur les sélecteurs. –

2

Pour répondre directement à votre question, non, le type SEL permet tout type de sélecteur, pas seulement ceux avec une signature spécifique.

Vous pouvez envisager le passage d'un objet au lieu d'un SEL, et le document que l'objet passé doit répondre à un message particulier. Par exemple:

- (void)execute:(id)object 
{ 
    // Do the execute stuff, then... 
    if ([object respondsToSelector:@selector(notifyOnExecute:)]) { 
     [object notifyOnExecute:self]; 
    } 
    // You could handle the "else" case here, if desired 
} 
+0

OK, merci. L'implémentation n'est pas facultative. Je pense que je le fais en utilisant des protocoles et des identifiants de type pour le "résultat" dans les rappels. Par exemple © protocole ActionCallback - (void) processData: (Action *) données d'action: (NSData *) ID de données: (NSUInteger) actionId; @end et de laisser l'implémentation gérer les ID d'action (dans une instruction switch). – MacTouch

Questions connexes