calling method with NSDictionary (userInfo) - iphone

I've got a timer
myTimer = [NSTimer scheduledTimerWithTimeInterval:x target:self selector:#selector(timerAction:) userInfo:[NSDictionary dictionaryWithObjectsAndKeys:y, #"y", z, #"z", nil] repeats:YES];
and method:
-(void)timerAction:(NSTimer *)theTimer {
SomeObject *y = [[theTimer userInfo] objectForKey:#"y"];
SomeObject *z = [[theTimer userInfo] objectForKey:#"z"];
{etc...}
NSLog(#"Done");}
Now my question is how to call the method directly? I need to turn it on immediately not only after x time passed, so i wanted to call it. However this method returns crash([__NSCFDictionary userInfo]: unrecognized selector sent to instance):
[self performSelector:#selector(timerAction:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:y, #"y", z, #"z", nil]];

As Vignesh suggested, you could just call the NSTimer with a time interval of 0. However, this could cause problems in the future.
You would be better to have a function that does the processing,
-(void)doProcessing:(NSDictionary *)theData {...
And then have a timer method
-(void)timerAction:(NSTimer *)timer {
[self doProcessing:[timer userInfo]];
}
So you can then call it through the timer as you currently have, or you can call it directly with [self doProcessing:data]
If you do it this way, then you will be much better placed if in the future you decide that after doing the processing you want to re-schedule the timer according to certain conditions but only if it was called through the timer as you can then do that in your timerAction method. You should try to get the functionality into as fine grained methods as possible, so don't mix the timer related bits with the processing bits.

Try this (before trying to call the selector) :
NSTimer* t = [[NSTimer alloc] init];
[[t userInfo] setObject:y forKey:#"y"];
[[t userInfo] setObject:x forKey:#"x"];
Then call it, either by :
[self performSelector:#selector(timerAction:) withObject:t];
or :
[self timerAction:t];

Why not do it like Vive wanted to originally, but do some introspection in the timerAction method to see what class the argument is:
-(void)timerAction:(id) param {
NSDictionary *dict = [param isKindOfClass:[NSDictionary class]]? param : [(NSTimer *)param userInfo];
id y = [dict objectForKey:#"y"];
id z = [dict objectForKey:#"z"];
NSLog(#"%# %#",y,z);
}

you can do this,
[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:#selector(timerAction:) userInfo:[NSDictionary dictionaryWithObjectsAndKeys:y, #"y", z, #"z", nil] repeats:NO];
myTimer = [NSTimer scheduledTimerWithTimeInterval:x target:self selector:#selector(timerAction:) userInfo:[NSDictionary dictionaryWithObjectsAndKeys:y, #"y", z, #"z", nil] repeats:YES];

Related

NSTimer Pause/ resume leak

I wanted to be able to Pause/ Resume my NSTimer and found this below answer:
NSDate *pauseStart, *previousFireDate;
-(void) pauseTimer:(NSTimer *)timer {
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFireDate = [[timer fireDate] retain];
[timer setFireDate:[NSDate distantFuture]];
}
-(void) resumeTimer:(NSTimer *)timer {
float pauseTime = -1*[pauseStart timeIntervalSinceNow];
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
[pauseStart release];
[previousFireDate release];
}
which works fine. However when testing my App for Leaks, it tells me that I get a leak in here:
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
Can anyone help me? You can see from the code i used that pauseStart and previousFireDate are retained in the pauseTime method and released in the resumeTimer method.
Many thanks
Sam
You should never call init (or any of the other methods in the same family) on an object twice. (Here's why). Change that line to this:
[timer setFireDate:[NSDate dateWithTimeInterval:pauseTime sinceDate:previousFireDate]];
and you'll be fine.

Updating NSDateFormatter with NSTimer

I have created a custom class which show time date formatter and I need a timer like method to update the seconds, so here is my code :
CustomClass.m
- (NSString *) showLocaleTime {
NSDateFormatter *timeFormater = [[NSDateFormatter alloc] init];
timeFormater = [setDateFormat:#"HH:mm:ss "];
NSString *currDay = [timeFormater stringFromDate:[NSDate date]];
currDay = [NSString stringWithFormat:#"%#",currDay];
[timeFormater release];
return timer;
}
- (void) updateLocaleTime {
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(showLocaleTime) userInfo:nil repeats:YES];
}
viewController.m :
CustomClass *time = [[CustomClass alloc]init];
label.text = [time showLocaleTime];
[time updateLocaleTime];
But the problem is the updateLocaleTime does not call to update seconds ! am I missing something ?
Thanks
Instead of calling updateLocaleTime in CustomClass, just start the timer in the view controller itself.
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(updateLocaleTime) userInfo:nil repeats:YES];
Add updateLocaleTime method to the viewController
- (void) updateLocaleTime {
CustomClass *time = [[CustomClass alloc]init];
label.text = [time showLocaleTime];
[time release];
}
But here we are allocating and releasing the CustomClass again and again for every 0.5 seconds. Instead you declare it as class member, in .h file and allocate that in viewDidLoad.
So no need to allocate in updateLocaleTime method. Also release that time in viewDidUnload method.
Where do you update the label text, after computing the new time? (You compute then new time but the it falls on the floor.)
Eg, add label.text = timer; to your showLocalTime method, and skip returning timer.

while loop memory problem

The problem with this code is that when the while loop is executing, the memory usage is increasing continuously. I want to know why does this code keeps on increasing memory when its in while loop.
Which object here is eating the memory. What can i do so that the memory does not increase in the loop.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSNumber *totalTime = [[self nowPlayingItem] valueForProperty:MPMediaItemPropertyPlaybackDuration];
while (self.currentPlaybackTime < [totalTime floatValue])
{
NSNumber *currentTime = [[NSNumber alloc] initWithFloat:self.currentPlaybackTime];
if([[NSThread currentThread] isCancelled])
{
[NSThread exit];
[newThread release];
}
else if([totalTime intValue] - [currentTime intValue] == 1.0)
{
if(currentTime)
[currentTime release];
break;
}
[currentTime release];
}
[pool release]
That is some inefficient code you have there.... Heres a replacement that doesn't go mental allocating memory for no reason, further more you might want to put a sleep in there so it doesn't eat CPU
// not needed any more
//NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int totalTime = [[[self nowPlayingItem] valueForProperty:MPMediaItemPropertyPlaybackDuration] intValue];
while (self.currentPlaybackTime < totalTime)
{
//not needed any more
//NSNumber *currentTime = [[NSNumber alloc] initWithFloat:self.currentPlaybackTime];
if([[NSThread currentThread] isCancelled])
{
[NSThread exit];
[newThread release];
}
else if((totalTime - self.currentPlaybackTime) <= 1.0) // you may never hit 1
{
break;
}
}
//[pool release]
I have solved this problem. Replaced the while loop with Timer and made few changes.
Created a timer which fires every second
timer = [NSTimer timerWithTimeInterval:1
target:self
selector:#selector(performAction)
userInfo:nil
repeats:YES];
then in performAction, check for the current playback time and invalidate the timer it the difference in time is <= 1 sec
int totalTime = [[[self nowPlayingItem] valueForProperty:MPMediaItemPropertyPlaybackDuration] intValue];
if((totalTime - self.currentPlaybackTime) <= 1.0)
{
if(timer)
[timer invalidate];
/* Performed my desired action here.... */
}

convert NSDictionary value to NSInteger

i have an NSDictionary that contain in value and i need to get this value
i have tried to get the value using the following code:
[NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(timerMovelabel:)
userInfo:
[NSDictionary dictionaryWithObject:var forKey:#"var1"]
, [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:23] forKey:#"var2"]
i have tried to get the value using the following methods
int intvar = [[[timer userInfo] objectForKey:#"var2"] intValue];
NSNumber *numbervar = [[timer userInfo] objectForKey:#"var2"];
NSInteger intvar = [num intValue];
Follows:
[self method:[[[timer userInfo] objectForKey:#"var2"] intValue]];
- (void)timerMovelabel:(NSTimer *)timer {
//here i execute one of the steps 1,2 and 3 but i didn't get any result all values are null
}
- (void) method:(NSInteger)dir
{
NSLog(#"%d",dir);
}
The setup of the timer appears to be wrong. You can not pass more than one dictionary to the userInfo parameter.
Try:
[NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(timerMovelabel:)
userInfo:
[NSDictionary dictionaryWithObjectsAndKeys: var, #"var1",
[NSNumber numberWithInt:23], #"var2",
nil]
repeats:NO];
EDIT: Added repeats parameter, thanks Bavarious.
Your userInfo is not built correctly. You need to pass only one object in there.
[NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(timerMovelabel:)
userInfo:
[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:23] forKey:#"var2"]
repeats:NO];
EDIT: If you would like to pass a dictionary with multiple keys and values, then you can do it with dictionaryWithObjects:forKeys: .
Moszi

NSArray Problem

i try to generate 6 random numbers and put them in a global NSArray, thats what i have done:
in MainViewController.h
NSArray * zufallsZahlen;
i have function to generate the Numbers:
- (NSArray *)generateNumbers {
NSMutableSet *s = [NSMutableSet set];
while([s count] < 6) {
NSNumber *z = [NSNumber numberWithUnsignedInteger:arc4random() % 46];
if(![s containsObject:z])
[s addObject:z];
}
NSArray *zahlen = [[s allObjects] sortedArrayUsingSelector:#selector(compare:)];
return zahlen;
}
Now in the ViewDidLoad:
zufallsZahlen = [self generateNumbers];
[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(onTimer:) userInfo:nil repeats:YES];
If i NSLog my zufallsZahlen NSArray in the ViewDidLoad i get the Output i want:
(
2,
8,
13,
16,
27,
31
)
The onTimer function creates every 0.2 seconds a ball with the actual Number:
-(void)onTimer:(NSTimer*)timer {
if (indexBall > 6){
//some function
}
else {
[self crateBall:[zufallsZahlen objectAtIndex:indexBall] ballId:indexBall ballX:xCoord ballY:100];
[self rollBall:indexBall rollToY: 80];
indexBall+=1;
xCoord-=40;
NSLog(#"%#", zufallsZahlen);
}
And if i Nslog the Array in the onTimer function i get the fooling Output:
Japanese.lproj
EDIT:
in viewDidLoad simply retain the NSArray:
zufallsZahlen = [self generateNumbers];
[zufallsZahlen retain];
You didn't retain the array. Remember that methods like [NSSet sortedArrayUsingSelector:] return an array that is autoreleased.
Hope that helps.
You can pass zufallsZahlen in the timer:
zufallsZahlen = [self generateNumbers];
[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(onTimer:) userInfo:zufallsZahlen repeats:YES];
- (void) onTimer:NSArray *zufallsZahlen {
// Do something with zufallsZahlen
}