web-dev-qa-db-fra.com

SEL performSelector et arguments

Il semble qu'il devrait y avoir un moyen facile d'appeler un sélecteur avec quelques arguments lorsque vous n'avez qu'un objet SEL. Je n'arrive pas à trouver la syntaxe correcte.

-(MyClass*) init: (SEL)sel owner:(NSObject*) parent
{
   int i =10;
   [parent performSelector:sel:i  ];
}
28
madmik3

Jetez un œil à la documentation NSObject . Dans ce cas:

[parent performSelector:sel withObject:[NSNumber numberWithInt:i]];

(notez que cette méthode est répertoriée dans la documentation NSObject protocol ). Puisque -[NSObject performSelector:withObject:] Nécessite un argument d'objet, vous devrez écrire un wrapper dans la classe du parent comme

-(void)myMethodForNumber:(NSNumber*)number {
    [self myMethod:[number intValue]];
}

pour déballer le NSNumber.

Si vous voulez vraiment invoquer une méthode qui prend directement des arguments non-objet (par exemple, vous n'avez pas le contrôle de la source appelée et vous ne voulez pas ajouter de catégorie), vous pouvez utiliser NSInvocation :

NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[parent methodSignatureForSelector:sel]];
[inv setSelector:sel];
[inv setTarget:parent];
[inv setArgument:&i atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
[inv invoke];

Sur une note latérale, votre méthode ressemble à une méthode init, mais ne suit pas le modèle d'initialisation correct pour Objective-C. Vous devez appeler l'initialiseur de super-classes, et vous devez tester un résultat nil à partir de cet appel et vous devez retourner self à partir de la méthode d'initialisation. Dans tous les cas, vos méthodes d'initialisation Objective-C devraient ressembler à:

-(id)myInitMethod {
    self = [super init];
    if(self != nil) {
      //perform initialization of self
    }

    return self;
}

Votre méthode (si c'est une méthode init) ressemblerait alors à:

-(id) init: (SEL)sel owner:(NSObject*) parent
{
   self = [super init];
   if(self != nil) {
       int i = 10;
       NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[parent methodSignatureForSelector:sel]];
       [inv setSelector:sel];
       [inv setTarget:parent];
       [inv setArgument:&i atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
       [inv invoke];
   }

    return self;
}

Pour être plus Objective-C stylistiquement, je renommerais aussi l'initialiseur -(id)initWithSelector:owner:.

76
Barry Wark

ce que Barry Wark a dit est super .. je viens de modifier la discussion pour les programmeurs paresseux

-(void)myMethodWith:(int)number andBOOL:(BOOL) someBool andStr:(NSString *)str{
    NSLog(@"%d %d %@",number,someBool,str);
}

-(void) testMethod{
    SEL sel = @selector(myMethodWith:andBOOL:andStr:);
    int i = 10;
    BOOL bol = YES;
    NSString *str = @"hey baby !";
    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:sel]];
    [inv setSelector:sel];
    [inv setTarget:self];
    [inv setArgument:&i atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
    [inv setArgument:&bol atIndex:3];
    [inv setArgument:&str atIndex:4];
    [inv invoke];
}
12
yunas

Vous souhaitez utiliser performSelector:withObject: La partie délicate consiste à convertir le int en NSObject. Vous ne pouvez pas utiliser performSelector avec des messages qui prennent int params, il doit plutôt prendre un id.

À partir de NSObject Protocol Reference :

aSelector doit identifier une méthode qui prend un seul argument de type id. Pour les méthodes avec d'autres types d'arguments et valeurs de retour, utilisez NSInvocation .

Une fois ce changement effectué, vous pouvez faire:

id arg = [NSNumber numberWithInt:10];    
[parent performSelector:sel withObject:arg];
3
Frank Krueger

Pour les méthodes qui prennent un ou deux objets de type id comme arguments, vous pouvez utiliser:

[parent performSelector:sel withObject:argument1];

ou

[parent performSelector:sel withObject:argument1 withObject:argument2];

Pour les méthodes avec d'autres types d'arguments, créez un NSInvocation qui peut encapsuler des appels de méthode arbitraires.

1
Ole Begemann

Vous pouvez utiliser:

- (id)performSelector:(SEL)aSelector withObject:(id)anObject
- (id)performSelector:(SEL)aSelector withObject:(id)anObject  withObject:(id)anotherObject

Ou si vous devez utiliser une méthode plus complexe, utilisez la classe NSInvocation

1
Skie