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
Related
I have the following method to update a label which show a simple time up
-(void)updateTimeLabel:(NSTimer *)timer{
NSInteger secondsSinceStart = (NSInteger)[[NSDate date] timeIntervalSinceDate:_startTime];
NSInteger seconds = secondsSinceStart % 60;
NSInteger minutes = (secondsSinceStart / 60) % 60;
NSInteger hours = secondsSinceStart / (60 * 60);
NSString *result = nil;
if (hours > 0) {
result = [NSString stringWithFormat:#"%02d:%02d:%02d", hours, minutes, seconds];
}
else {
result = [NSString stringWithFormat:#"%02d:%02d", minutes, seconds];
}
_totalTime = result;
_totalTimeLabel.text = result;
}
I then call this as the action to a button:
-(IBAction) startTimer{
_startTime = [NSDate date];
_walkRouteTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimeLabel:) userInfo:nil repeats:YES];
[_walkRouteTimer fire];
}
But when I run the app I get a bad access error and the app crashes, can anyone help me with this?
Thanks in advance
Are you using ARC? If not, _startTime = [NSDate date]; this line will cause your problem. [NSDate date] returned an autorelease object and _startTime will not hold it if you are not using ARC(or using ARC but declared _startTime as weak).
If so, try to add a retain to it
-(IBAction) startTimer{
//_startTime = [NSDate date]
_startTime = [[NSDate date] retain];
_walkRouteTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimeLabel:) userInfo:nil repeats:YES];
[_walkRouteTimer fire];
}
And when you finished your timer, after calling of [_walkRouteTimer invalidate], call [_startTime release].
Or even simpler, if you use property for startTime and declared it as retain. Just use dot notation:
-(IBAction) startTimer{
//_startTime = [NSDate date]
self.startTime = [NSDate date];
_walkRouteTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimeLabel:) userInfo:nil repeats:YES];
[_walkRouteTimer fire];
}
...
//After [_walkRouteTimer invalidate]
self.startTime = nil;
Try adding an exception breakpoint to see which line is crashing:
1) Click on the breakpoint tab (second from the right)... kinda looks like a right pointing arrow or "next" button
2) Click on the "+" in the bottom left corner of the tab menu
3) Select "Add Exception Breakpoint"
4) (Optional) Select "Exception" drop down and change to "Objective-C"
5) Select "Done"
6) Run your code again and try to generate the crash... when you do, it will hopefully be caught by this breakpoint, and you'll see which line is crashing and be able to fix it
Good luck
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];
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.
I have a problem with a NSTimer.
I received a "SIGABRT" error and [NSCFTimer intValue]: unrecognized selector sent to instance
These is my code:
-(void)detectionMove:(NSNumber*)arrayIndex{
static BOOL notFind = FALSE;
static int countVariable = 0;
static int countRilevamenti = 0;
notFind = FALSE;
for(int i = countVariable+1; i<[[[[sharedController arrayMovement]objectAtIndex:[arrayIndex intValue]] arrayPositionMove]count]; i++){
if(!notFind){
if((actualAccelerometerX+sensibilityMovement) >= [[[[[sharedController arrayMovement]objectAtIndex:[arrayIndex intValue]] arrayPositionMove]objectAtIndex:i]valueX] && (actualAccelerometerX-sensibilityMovement) <= [[[[[sharedController arrayMovement]objectAtIndex:[arrayIndex intValue]] arrayPositionMove]objectAtIndex:i]valueX] &&
(actualAccelerometerY+sensibilityMovement) >= [[[[[sharedController arrayMovement]objectAtIndex:[arrayIndex intValue]] arrayPositionMove]objectAtIndex:i]valueY] && (actualAccelerometerY-sensibilityMovement) <= [[[[[sharedController arrayMovement]objectAtIndex:[arrayIndex intValue]] arrayPositionMove]objectAtIndex:i]valueY] &&
(actualAccelerometerZ+sensibilityMovement) >= [[[[[sharedController arrayMovement]objectAtIndex:[arrayIndex intValue]] arrayPositionMove]objectAtIndex:i]valueZ] && (actualAccelerometerZ-sensibilityMovement) <= [[[[[sharedController arrayMovement]objectAtIndex:[arrayIndex intValue]] arrayPositionMove]objectAtIndex:i]valueZ])
{
countVariable = i;
notFind = TRUE;
countRilevamenti++;
}
}
}
if(!notFind)
return;
else if(countVariable+1 == [[[[sharedController arrayMovement]objectAtIndex:[arrayIndex intValue]] arrayPositionMove]count]){
if(countRilevamenti + tollerance >= [[[[sharedController arrayMovement]objectAtIndex:[arrayIndex intValue]] arrayPositionMove]count])
movementDetected = [arrayIndex intValue];
else
NSLog(#"troppo veloce");
countVariable = 0;
notFind = FALSE;
countRilevamenti = 0;
return;
}
[NSTimer scheduledTimerWithTimeInterval:timeToCatch target:self selector:#selector(detectionMove:) userInfo:(NSNumber*)arrayIndex repeats:NO];
}
You have the wrong signature for your method
- (void)timerFireMethod:(NSTimer*)theTimer
not NSNumber
--edit2--
NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];
[myDictionary setObject:arrayIndex forKey:#"index"];
[NSTimer scheduledTimerWithTimeInterval:timeToCatch target:self selector:#selector(detectionMove:) userInfo:myDictionary repeats:NO];
--edit--
If you want to keep your old method so you can call it from somewhere else with a NSNumber argument you have to create a new method for the NSTimer to call and then in the implementation of the NSTimer method you call the NSNumber method with whatever number that is appropriate.
-(void)detectionMove:(NSNumber*)arrayIndex{
// still does whatever
}
-(void)automaticDetectionMove:(NSTimer*)theTimer{
[self detectionMove:whatevernumber];
}
// update with new method name
[NSTimer scheduledTimerWithTimeInterval:timeToCatch target:self selector:#selector(automaticDetectionMove:) userInfo:(NSNumber*)arrayIndex repeats:NO];
NSTimer fire methods must have the signature:
- (void)myMethod:(NSTimer *)timer;
The timer passes itself as an argument to the method you set.
If you want extra parameters, set them in the user info dictionary, and then retrieve them using their key using:
[[timer userInfo] objectForKey:#"myUserInfoParamterKey"];
So as an example, you should be setting your NSNumber object in your user info and retrieving it that way, not passing it as a parameter to the timer method.
as pointed out by several other answers, the NSTimer signature is
- (void)methodName:(NSTimer *)timer;
if you need to call a method sometimes directly, sometimes via a timer (or perhaps with dictionary that contains a key to the argument you need), rather than wrapping, you may want to consider adding a set of helper macros to put in your project .pch file
#define ifThenElse(_if_,_then_,_else_) ( (_if_) ? _then_ : _else_ )
#define isClass(objectToTest,isOfClass) ifThenElse(objectToTest==nil,NO,[objectToTest isKindOfClass:[isOfClass class]])
#define asClass(object,requiredClass) \
/**/ifThenElse(isClass(object,requiredClass),\
/*****/((requiredClass *)object),\
/****/nil\
/***/)
#define asClassViaMethod(object,requiredClass,viaClass,viaMethod) \
/**/ifThenElse(isClass(object,requiredClass),\
/*****/(requiredClass *)object,\
/*****/ifThenElse(isClass(object,viaClass),\
/*********/(requiredClass *)[(viaClass*)object viaMethod],\
/*********/nil)\
/***/)
the way you use them is relatively simple, and imho makes for more readable code than wrapping a function 3 times to account for variant invocations.
but first some background info on how it works.
your timer method selector can be expressed quite safely in it's most basic form,
- (void)myMethod:(id)sender;
without anything nasty happening on the way. all it means is that "sender" has no object type, and you can't do a lot with it, without casting it to the type you expect it to be.
before you can safely cast however, you need to know for sure that the object is of the correct type.
thats where the macros help in that they wrap the sender with a series of tests that filters out incorrect types and return nil instead of allowing you to send an incorrect selector to an object that can't respond to it. please note that the macros themselves can be expressed in a more concise manner, however for understanding how they work, and without any performance overhead, i have chosen to express them longhand with comments. you can always trim them down, however there is no real performance benefit, as they will result in the same compiled output.
theoretical use examples:
NSTimer recipient - you want an NSNumber to work with, but timer sends you itself.
- (void)myMethod:(id)sender{
NSNumber *argument = asClassViaMethod(sender,NSNumber,NSTimer,userInfo);
}
UIGestureRecognizer recipient - you want the view to work with, by GR sends you itself.
- (void)myMethod:(id)sender{
NSNumber *argument = asClassViaMethod(sender,UIView,UIGestureRecognizer,view);
}
Custom method you are extending, but don't want to break it
it used to be:
- (void)myMethod:(NSNumber *)number{
[myOtherObject setNumber:number];
}
but it now needs an NSNumber & an NSString, instead of just an NSNumber. you also may sometimes just want to send it an NSString by itself
adjusted method header and argument parsers
- (void)myMethod:(id)sender{
NSNumber *number = asClassViaMethod(sender,UIView,NSDictionary,objectForKey:#"number");
NSNumber *string = asClassViaMethod(sender,UIView,NSDictionary,objectForKey:#"string");
if (number) {
// do something funky with number
}
if (string) {
// do something funky with string
}
}
real world use examples cut and paste from one of my projects:
NSInteger artistIndex = asClassViaMethod(artistIndex_, NSNumber, NSTimer, userInfo).integerValue;
...
NSDictionary *payload = asClassViaMethod(sender,NSDictionary,NSTimer,userInfo);
as a side note, this is a handy way of sending multiple parameters to a timer method, when you want the flexibility of calling that method directly, without a wrapper...
UIView *tappee = asClassViaMethod(sender,UIView,UITapGestureRecognizer,view);
...
for (id element in connectArray) {
NSURL *url = asClassViaMethod(element, NSURL, NSDictionary, objectForKey:#"url");
NSArray *additionalHeaders = asClassViaMethod(element, NSArray, NSDictionary, objectForKey:#"headers");
NSString *path = [url path];
NSString *query = [url query];
if (query.length) {
path = [[url path] stringByAppendingFormat:#"?%#",query];
}
[self requestDocument:path additionalHeaders:additionalHeaders];
}
in this last example, if the array contains an NSURL, it uses that, with "additionalHeaders" being resolved to nil.
if the array contains an NSDictionary containing an NSURL and an NSArray with the keys #"url" and #"headers" respectively, it uses the supplied values.
finally, in case it's not obvious, calling your timer method
[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(myMethod:) userInfo:[NSNumber numberWithInteger:myNSInteger] repeats:NO];
... or ...
[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(myMethod:) userInfo:[NSDictionary dictionaryWithObjectsAndKeys: myNSNumber,#"number",myNSString,#"string",nil] repeats:NO];
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
}