Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way of passing a primitive argument with an NSTimer

I'm using a basic timer that calls this method:

- (void) refresh:(id)obj
{
    if (obj == YES) doSomething;
}

I want to call this method from certain areas of my code and also from a timer

[NSTimer scheduledTimerWithTimeInterval:refreshInterval
                            target:self
                            selector:@selector(refresh:) 
                            userInfo:nil 
                            repeats:YES];

When I put YES as the argument for the userInfo parameter, I get an EXC_BAD_ACCESS error; why is this?

Can someone help me do this the right way so that there is no ugly casting and such?

like image 350
Mark Avatar asked Jan 23 '26 00:01

Mark


2 Answers

The userInfo parameter must be an object; it is typed id. YES is a primitive, namely the value 1. In order to make sure the userInfo object does not get deallocated, the timer retains it. So, when you passed YES, NSTimer was doing [(id)YES retain]. Try that in your own code and see what happens. :-P

As the Documentation states, the selector you give the method must have the signature

- (void)timerFireMethod:(NSTimer*)theTimer

This means you can't have an NSTimer invoke just any method—not directly at least. You can make a special method with the above signature which in turn invokes any method you want though.

So, say you have a method called refresh:, and you want to call it every so often, passing it YES. You can do this like so:

// somewhere
{
    [NSTimer scheduledTimerWithTimeInterval:refreshInterval
                                     target:self
                                   selector:@selector(invokeRefresh:)
                                   userInfo:nil
                                    repeats:YES];
}

- (void)invokeRefresh:(NSTimer *)timer {
    [self refresh:YES];
}

- (void)refresh:(BOOL)flag {
    if (flag) {
        // do something
    }
}
like image 141
kperryua Avatar answered Jan 25 '26 09:01

kperryua


In Objective-C, a primitive type is not an object. So you can't directly pass it to an argument which expects an id, which stands for a generic object. You need to wrap it into an NSNumber object.

Use

NSTimer*timer=[NSTimer scheduledTimerWithTimeInterval:refreshInterval
                                 target:self
                               selector:@selector(refresh:) 
                               userInfo:[NSNumber numberWithBool:YES]
                                repeats:YES];

and

- (void) refresh:(NSTimer*)timer
{
    NSNumber* shouldDoSomething=[timer userInfo];
    if ([shouldDoSomething boolValue]) doSomething;
}

Don't forget to invalidate and release the timer once it's done.

By the way, you don't have to compare a BOOL (or C++ bool) against YES or true or whatever. When you write

if(a>b) { ... }

a>b evaluates to a bool, and if uses the result. What you're doing there is like

if((a>b)==YES) { ... }

which is quite strange to me. It's not that the (..) after if should contain a comparison; it should contain a bool.

like image 25
Yuji Avatar answered Jan 25 '26 09:01

Yuji



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!