user2057209
user2057209

Reputation: 345

Multiple parameters for @selector

I am creating a game in objective C, and I am stopped by a matter : I have a warning for passing multiple variables on @selector. What I want to do, is call a method in my UIViewController but after a delay. So I try to make a first method, that call the other after delay like this :

-(void)AnimationCoinInitWith_x:(int)x y:(int)y w:(int)w h:(int)h afterDelay:(NSTimeInterval)t
{
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
                                [self methodSignatureForSelector:@selector(AnimationCoinCopyInitWith_x:y:w:h:)]];
    [invocation setTarget:self];
    [invocation setSelector:@selector(AnimationCoinCopyInitWith_x:y:w:h:)];
    [invocation setArgument:x atIndex:1];
    [invocation setArgument:y atIndex:2];
    [invocation setArgument:w atIndex:3];
    [invocation setArgument:h atIndex:4];
    [NSTimer scheduledTimerWithTimeInterval:t invocation:invocation repeats:NO];
}

-(void)AnimationCoinCopyInitWith_x:(int)x y:(int)y w:(int)w h:(int)h
{
    UIImageView* imageViewCoin = [[UIImageView alloc] initWithFrame:CGRectMake(x, y, w, h)];
    [imageViewCoin setAnimationImages:images];
    [imageViewCoin setAnimationRepeatCount:1000];
    [imageViewCoin setAnimationDuration:(1/24)];
    [imageViewCoin startAnimating];
    [self addSubview:imageViewCoin];
    [imageViewCoin release];
}

But it's not working, I don't know why.

Thanks for your help !

Upvotes: 0

Views: 1007

Answers (2)

bbum
bbum

Reputation: 162712

As a general rule, we adopted a policy of avoiding NSInvocation except when absolutely necessary because the code tends to be hard to read, fragile, and a headache when refactoring (and a source of bugs).

Also, by avoiding NSInvocation and using straightforward call sites, the compiler is fully able to validate the code.

You can use the "dispatch_after" pattern:

    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
     [self animationCoinInitWith_x: ...];
    });

Or, if you want to stick with -performSelector:withObject:afterDelay: (which I find more readable):

@interface Foo : NSObject
@end

@implementation Foo
- (void)invokeBlock:(dispatch_block_t)aBlock
{
    aBlock();
}

- (void)doIt:(int)x toIt:(int)y
{
    NSLog(@"%s %d %d", __PRETTY_FUNCTION__, x, y);
}
@end


int main(int argc, const char * argv[])
{
    @autoreleasepool {
        Foo* foo = [Foo new];
        dispatch_block_t block = ^{
            [foo doIt:10 toIt:10];
        };
        [foo performSelector:@selector(invokeBlock:) withObject:[block copy] afterDelay:5];
        [[NSRunLoop currentRunLoop] run];
    }
    return 0;
}

The above assumes ARC (hence, the lack of releases).

Upvotes: 0

Richard J. Ross III
Richard J. Ross III

Reputation: 55533

Here, your problem is that NSInvocation doesn't automagically set the offsets of arguments you need, and as all objective-c methods have two invisible arguments (self and _cmd), you must offset your argument indices by 2, not 1.

Another issue here is that you are failing to pass the arguments by reference, so you must use the address operator (&):

[invocation setArgument:&x atIndex:2];
[invocation setArgument:&y atIndex:3];
[invocation setArgument:&w atIndex:4];
[invocation setArgument:&h atIndex:5];

Once you do that, your code above should work fine.

Upvotes: 4

Related Questions