views:

63

answers:

3

Im using a basic timer that calls a method which is defined as below:

- (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 EXEC_BAD_ACCESS error why is this?

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

Thanks Mark

+6  A: 

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
    }
}
kperryua
+1 beat me to it!
Abizern
A: 

As a follow-up to kperryua's answer, if you want to pass a primitive through userInfo you can box it with NSNumber or NSValue; in the case of a boolean, you'd want to use [NSNumber numberWithBool:YES], then call boolValue in the timer callback to get back the primitive.

smorgan
+2  A: 

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.

Yuji