2009-11-11 4 views
0

Je voudrais (à l'exécution) pour lier un paramètre à une fonction que vous pouvez faire en boost :: bind - un peu comme ce qui suit:paramètre Objectif d'exécution C liant

-(void)myFuncWithParameter:(NSString*)param { 
    NSLog(param); 
} 

-(void)init { 

    UIButton *helloButton = [UIButton buttonWithType:UIButtonTypeCustom]; 
    [helloButton addTarget:self action:@selector(myFuncWithParameter:@"hello") forControlEvents:UIControlEventTouchUpInside]; 

} 

So. .. Je lie dynamiquement (à l'exécution) la valeur @ "bonjour" à un paramètre.

De toute évidence, ce qui précède n'est pas la bonne syntaxe. Est-ce que quelqu'un sait si c'est possible et la syntaxe correcte?

Cheers, Nick.

+0

Après coup - je pourrais ajouter un KVP à la couche du bouton [helloButton.layer setValue: @ "hello" forKey: @ "param1"]; ... se référer au bouton quand il est passé à la fonction appelée ... mais c'est une solution assez moche! –

Répondre

1

La réponse générale est que le mécanisme cible-action ne permet qu'une cible, un expéditeur et un message qui prend l'expéditeur; par conséquent, si vous devez accéder à des données, vous devez les obtenir auprès de la cible ou de l'expéditeur. Une option consisterait à créer une classe représentant la liaison d'une valeur de paramètre, d'une méthode et d'un objet. Cette classe aurait une action qui appelle la méthode sur l'objet, en passant la valeur. Utilisez une instance de cette classe comme cible. Voici un exemple simpliste:

@interface UnaryBinder : NSObject { 
    id target; 
    SEL selector; 
    id parameter; 
} 
@property id target; 
@property SEL selector; 
@property (retain) id parameter; 
-(id)initWithTarget:(id)anObject selector:(SEL)aSelector param:(id)aParameter; 
-(void)action:(id)sender; 
@end 

@implementation UnaryBinder 
... 
-(void)action:(id)sender { 
    [target performSelector:selector withObject:parameter]; 
} 
@end 

Si vous souhaitez prendre en charge un nombre arbitraire de paramètres, vous aurez besoin d'utiliser NSInvocation (comme Louis mentionne) plutôt que performSelector:withObject. Bien sûr, les contrôles ne conservent pas leurs cibles, vous avez donc besoin d'un moyen de conserver l'UnaryBinder. À ce stade, vous pouvez aussi ignorer la classe spéciale et simplement stocker les données dans le contrôle, comme vous le mentionnez dans votre commentaire sur l'utilisation de KVP. Vous pouvez également factoriser l'action dans une classe de contrôleur et utiliser une instance de celle-ci comme cible. UnaryBinder et ses semblables n'apportent vraiment aucun avantage quand il s'agit de cibler-action. Pour les sujets connexes, google "messagerie d'ordre supérieur".

+0

Deux réponses probablement aussi bonnes, mais je l'ai donné ici car il est légèrement plus simple à comprendre et à mettre en œuvre! N –

+0

"Bien sûr, les contrôles ne conservent pas leurs cibles, vous avez donc besoin d'un moyen de conserver l'UnaryBinder." C'est le problème avec cette solution – user102008

+0

@ user102008: c'est seulement un "problème" en ce sens que stocker un 'UnaryBinder' n'est pas explicitement adressé dans ma réponse, c'est pourquoi je l'ai mentionné. De plus, la seule raison pour laquelle il n'y a pas de solution explicite est qu'il n'y a pas de solution universelle, et Nick n'a pas donné beaucoup de détails sur sa conception spécifique; comment 'UnaryBinder' est stocké dépend des besoins de conception.Un 'UnaryBinder' peut facilement être stocké dans un contrôleur (par exemple), mais il n'y a pas un contrôleur spécifique mentionné dans la question qui servirait. – outis

3

La réponse courte est non, ou du moins pas à ce niveau. La réponse longue est qu'il est techniquement possible de construire quelque chose comme utiliser NSInvocations (et/ou forwardInvocation:), faire quelque chose d'intelligent dans methodForSelector: et ou en enregistrant dynamiquement les implémentations de méthodes, mais c'est très compliqué, surtout si vous se soucient de la vitesse. Si j'avais un code où construire des méthodes curry comme ça en valait vraiment la peine, ce que je ferais, c'est quelque chose comme ça (écrit dans ce commentaire, non testé);

//FIXME: In a real implementation you would do some mangling, this code will get confused if you have _s in the curried selector, and thus could be exploitable 

//This method makes a unique selector by mangling the arguments 
- (SEL) selectorForSelector:(SEL)bindSel withString:(NSString *)bindString { 
    NSString *mangle = [NSString *stringWithFormat:@"LGBind_%@_%@"], NSStringFromSelector(bindSel), bindString]; 
    SEL retval = NSSelectorFromString(mangle); 

    //Register the imp. You probably want to check if it is already reg 
    if (![self respondsToSelector:retval]) { 
    class_addMethod([self class], retval, LGBind_IMP, "[email protected]:")l 
    } 
} 

//Generic dispatcher imp 
void LGBind_IMP(id self, SEL _cmd) { 
    NSString *selectorName = NSStringFromSelector(_cmd); 
    NSArray *array [selectorName componentsSeparatedByString:@"_"]; 

    //Skip index 0; it is @"LGBind" 
    NSString *originalSelectorString = [array objectAtIndex:1]; 
    NSString *originalArgString = [array objectAtIndex:2]; 

    //Get our the SEL and the IMP 
    SEL originalSEL = NSSelectorFromString(originalSelectorString); 
    IMP originalIMP = [self methodForSelector:originalSEL]; 

    //call the original imp 
    originalIMP([self class], originalSEL, originalArgString); 
} 

De toute évidence, en fonction de vos besoins que vous pourriez faire des choses quelque part différemment, par exemple, vous pourriez paresseusement par les garnements forwardInvocation ou les données Stash sur le sélecteur managled dans un dict dans l'instance au lieu de simplement managling dans le nom du sélecteur.