Accessing Instance Variables from NSTimer selector
Firstly newbie question: What's the difference between a selector and a method?
Secondly newbie question (who would have thought): I need to loop some code based on instance variables and pause between loops until some condition (of course based on instance variables) is met. I've looked at sleep, I've looked at NSThread. In both discussions working through those options many asked why don't I use NSTimer, so here I am.
Ok so it's simple enough to get a method (selector? ) to fire on a schedule. Problem I have is that I don't know how to see instance variables I've set up outside the timer from within the code NSTimer fires. I need to see those variables from the NSTimer selector code as I 1) will be updating their values and 2) will set labels based on those values.
Here's some c开发者_开发知识库ode that shows the concept… eventually I'd invalidate the timers based on myVariable too, however I've excluded that for code clarity.
MyClass *aMyClassInstance = [MyClass new];
[aMyClassInstance setMyVariable:0];
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doStuff) userInfo:nil repeats:YES];
[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(doSomeOtherStuff) userInfo:nil repeats:YES];
- (void) doStuff {
[aMyClassInstance setMyVariable:11]; // don't actually have access to set aMyClassInstance.myVariable
[self updateSomeUILabel:[NSNumber numberWithInt:aMyClassInstance.myVariable]]; // don't actually have access to aMyClassInstance.myVariable
}
- (void) doSomeOtherStuff {
[aMyClassInstance setMyVariable:22]; // don't actually have access to set aMyClassInstance.myVariable
[self updateSomeUILabel:[NSNumber numberWithInt:aMyClassInstance.myVariable]]; // don't actually have access to aMyClassInstance.myVariable
}
- (void) updateSomeUILabel:(NSNumber *)arg{
int value = [arg intValue];
someUILabel.text = [NSString stringWithFormat:@"myVariable = %d", value]; // Updates the UI with new instance variable values
}
You can use the userInfo
parameter to transmit arbitrary object. In this case, you pass aMyClassInstance
as userInfo:
MyClass *aMyClassInstance = [MyClass new];
[aMyClassInstance setMyVariable:0];
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doStuff) userInfo:aMyClassInstance repeats:YES];
In the timer callback (which MUST takes a parameter), you get back the userInfo
from the timer and cast it:
- (void) doStuff:(NSTimer *)timer {
MyClass *instance = (MyClass *)[timer userInfo];
[instance setMyVariable:11];
[self updateSomeUILabel:[NSNumber numberWithInt:instance.myVariable]];
}
The neat thing is that the timer retains the userInfo parameter.
One of your questions was asking about the difference between a selector and a method.
A selector “selects” the method to use from an object. Imagine you had some animal classes, say Dog
, Cat
, and Bird
, all subclasses of Animal
. They all implement a method called makeSound
. Each class will have its own implementation of makeSound
, otherwise all of the animals will sound the same. So, all animals have a different method for making a sound, but you get each animal to make its sound using the same selector. You are selecting the makeSound
method of an animal, in other words.
You do have access to instance variables if you set the instance as the target of the timer like so:
[NSTimer scheduledTimerWithTimeInterval:1.0 target:aMyClassInstance selector:@selector(doStuff) userInfo:nil repeats:YES];
The instance (which you've referred to as aMyClassInstance
) will be self
.
Alternatively you can put aMyClassInstance
and any other objects in the userInfo
dictionary. You would do that like so:
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
aMyClassInstance, @"a",
bMyClassInstance, @"b",
cMyClassInstance, @"c",
nil];
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doStuff:) userInfo:userInfo repeats:YES];
Then, in the doStuff:
selector, you can get them back out like so:
-(void) doStuff:(NSTimer*)timer;
{
MyClass* aMyClassInstance = [[timer userInfo] objectForKey:@"a"];
MyClass* bMyClassInstance = [[timer userInfo] objectForKey:@"b"];
MyClass* cMyClassInstance = [[timer userInfo] objectForKey:@"c"];
//do whatever you want here
}
精彩评论