Question

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 !

Was it helpful?

Solution

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.

OTHER TIPS

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).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top