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?
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
}
}
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.
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.