tags:

views:

244

answers:

3

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 code 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
}
A: 

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
}
Tom Dalling
Interesting ... is there a way to get access to instance variables of multiple objects? For example if I had variables that would effect the outcome of the timed event in aMyClassInstance, bMyClassInstance, cMyClassInstance, someOtherClassInstance etc etc?
Timbo
You just need to keep hold of the pointers to them somewhere. You can make them all ivars of the target, or you can put them all into an `NSDictionary` that you pass as the `userInfo` argument. You wont have direct access to the instance variables of all the objects, but you'll have access to the public interface of the objects which can be used to achieve the same end.
Tom Dalling
Thanks for the info, I'm trying to work through the ins and outs of NSDictionary now. Are you able to give an example of how I would use NSDictionary to store a pointer to aMyClassInstance in an NSDictionary, and how I'd reference that dictionary from the method NSTimer calls?
Timbo
I've updated the answer with an example.
Tom Dalling
Ah excellent, thanks :)
Timbo
+2  A: 

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.

Laurent Etiemble
Is there a way to get access to instance variables of multiple instances?
Timbo
Yep. Just wrap them in a NSDictionary, pass the dictionary as userInfo and retrieve them at will.
Laurent Etiemble
You're too good, thankyou so much for your help :)
Timbo
Would I wrap just the variables I want into NSDictionary or do I put a pointer to the whole aMyClassInstance object in there? Either way, when I set the variable via NSDictionary from the NSTimer called method, will it set the actual instance variable? Something like NSMutableDictionary *myDictionary = [NSMutableDictionary new]; [myDictionary setObject:aMyClassInstance forKey:@"aMyClassInstance"];
Timbo
Yes, this is what I meant (putting the instance pointer in the dictionary).As aMyClassInstance is a pointer to the MyClass instance, if you put it into the dictionary and retrieve it later, you will modify the same instance. If you want to be sure, just put a breakpoint in your code to check it out.
Laurent Etiemble
excellent, thanks for the clarification
Timbo
+1  A: 
dreamlax
Ok that makes sense, thankyou!
Timbo