Probelem with NSTimer - iphone

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];

Related

Spotted a leak in UITextView delegate method. Confused about solution

I've got a problem with an UITextView and one of its delegate methods in my navigation based app:
- (BOOL)textView:aView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
I managed to limit the max length of text the user can input using the above method. But I'm using a leaky array for that matter I think.
The problem is:
I want to save the amount of typed characters right in the very moment the user enters the last line of my textview. I then use that value to calculate the string length - which I compare to the textview's content size to set a limit. The code works fine - but since the method it's inside of is updating with every text input, I'm having trouble releasing the array in the right moment.
Here's some code:
if (numLines == 9)
{
if (!numCharsArray)
{
numCharsArray = [[NSMutableArray alloc] initWithCapacity:1]; // Stack trace gives this line 3,3% of the leak.
}
numChars = tView.text.length;
NSNumber *number = [[NSNumber alloc] initWithInteger:numChars]; // This line gets 77,3%.
[numCharsArray addObject:number]; // This line gets the rest, 24,3%.
[number release];
startChars = [[numCharsArray objectAtIndex:0] integerValue];
NSString *lastLine = [[NSString alloc]initWithString:[[tView text] substringFromIndex:startChars]];
CGSize lineSize = [lastLine sizeWithFont:tView.font forWidth:tView.contentSize.width lineBreakMode:UILineBreakModeWordWrap];
[lastLine release];
if (range.length > text.length)
{
return YES;
}
else if (numLines == 9 && lineSize.width >= tView.contentSize.width - 45)
{
return NO;
}
}
else
{
numCharsArray = nil;
/*
if(numCharsArray)
{
[numCharsArray release];
}
*/
}
I tried the out-commented statement above, but that gives me an app crash once I leave the last line of the textview. And as you can see in the code comments - without releasing the array I get a leak.
So how and where do I release that array correctly - keeping it safe while the user is on the last line?
Just replace with
first one
numCharsArray = [NSMutableArray array]; // you do not need to release
//explicitly as its autorelease numberWithInt
second one
NSNumber *number = [NSNumber numberWithInt:numChars]; //autorelease
NSString *lastLine = [[tView text] substringFromIndex:startChars];

how do I set the value of an NSNumber variable (without creating a new object) in objective-c

how do I set the value of an NSNumber variable (without creating a new object) in objective-c?
Background
I'm using Core Data and have a managed object that has an NSNumber (dynamic property)
passing (by reference) this to another method which will update it
not sure how to update it? if I allocate it another new NSNumber things don't work, which I guess makes sense it's then got a pointer to a different object not the core data object (I guess)
An NSNumber object isn't mutable. This means that the only way to change a property containing a NSNumber is to give it a new NSNumber. To do what you want, you have three options:
1. Pass the Core Data object to the method and have it directly set the property.
- (void)updateNumberOf:(MyCoreDataObject *)theObject {
NSNumber *newNumber = ...; // create new number
theObject.number = newNumber;
}
Called as [self updateNumberOf:theCoreDataObject];
2. Have the update method return a new NSNumber and update it in the caller.
- (NSNumber *)updateNumber:(NSNumber *)oldNumber {
NSNumber *newNumber = ...; // create new number
return newNumber;
}
Called using:
NSNumber *theNumber = theCoreDataObject.number;
theNumber = [self updateNumber:theNumber];
theCoreDataObject.number = theNumber;
3. Pass a pointer to a number variable and update it in the caller (I would only suggest this over option 2 if you need to return something else).
- (void)updateNumber:(NSNumber **)numberPointer {
if(!numberPointer) return; // or possibly throw an error
NSNumber *oldNumber = *numberPointer;
NSNumber *newNumber = ...; // create new number
*numberPointer = newNumber;
}
Called using:
NSNumber *theNumber = theCoreDataObject.number;
[self updateNumber:&theNumber];
theCoreDataObject.number = theNumber;
I did not bother with memory management in any of these examples. Make sure you release/autorelease objects appropriately.
4. (from Greg's comment) Similar to option 1, but passes the key to the update method to be more portable.
- (void)updateNumberOf:(id)theObject forKey:(NSString *)key {
NSNumber *oldNumber = [theObject valueForKey:key];
NSNumber *newNumber = ...; // create new number
[theObject setValue:newNumber forKey:key];
}
Called as [self updateNumberOf:theCoreDataObject forKey:#"number"];

Change UILabel in loop

I want to change the UILabel after 2 sec in a loop.
But current code change it to last value after loop finished.
- (IBAction) start:(id)sender{
for (int i=0; i<3; i++) {
NSString *tempStr = [[NSString alloc] initWithFormat:#"%s", #" "];
int randomNumber = 1+ arc4random() %(3);
if (randomNumber == 1) {
tempStr = #"Red";
}else if (randomNumber == 2) {
tempStr = #"Blue";
} else {
tempStr = #"Green";
}
NSLog(#"log: %# ", tempStr);
labelsText.text = tempStr;
[tempStr release];
sleep(2);
}
}
Your code updates label to last value only as your function blocks main thread so UI cannot get updated. To solve that problem move your updating code to separate function and call it using performSelector:withObject:afterDelay: method. (or schedule calls using NSTimer)
Possible solution (you will also need to handle the case when user taps your button several times in a row, but that should not be too difficult):
- (IBAction) start:(id)sender{
[self updateLabel];
}
- (void) updateLabel{
static const NSString* allStrings[] = {#"Red", #"Blue", #"Green"};
static int count = 0;
int randomNumber = arc4random()%3;
NSString *tempStr = allStrings[randomNumber];
NSLog(#"log: %# ", tempStr);
labelsText.text = tempStr;
++count;
if (count)
[self performSelector:#selector(updateLabel) withObject:nil afterDelay:2.0];
}
- (IBAction) start:(id)sender{
for (int i=0; i<3; i++) {
int randomNumber = 1+ arc4random() %(3);
NSString *tempStr = #"";
if (randomNumber == 1) {
tempStr = #"Red";
}else if (randomNumber == 2) {
tempStr = #"Blue";
} else {
tempStr = #"Green";
}
[labelsText performSelector:#selector(setText:) withObject:tempStr afterDelay:i * 2]
NSLog(#"log: %# ", tempStr);
}
}
Don't use sleep() to perform actions after a delay, it blocks the whole thread. Use performSelector:withObject:afterDelay: instead. As you are probably running this on the main thread, sleep() will block any UI updates until after the whole loop has run, so the only thing you see is the last update. As a general rule of thumb, assume that the UI doesn't ever get updated until after the app has finished executing your method.
You shouldn't use %s as the format specifier for an NSString, that's the format specifier for a C string. You should use %# instead. In fact, if all you are doing is initialising the string with an NSString literal, there's no need to use initWithFormat at all, you can just use the literal itself.
You've also got big memory problems. At the beginning of the loop, you allocate memory for an instance of NSString that is a single space. You then overwrite the pointer to this memory when you assign to tempStr again, meaning you leak the original allocation of memory. Build and Analyze will find problems like this for you. Then you release tempStr, but as the second assignment to this pointer variable was to an autoreleased NSString, the instance will be released one time too many when the autorelease pool gets drained, which will probably manifest itself as a crash that's impossible to debug a little later in the app.
I'd do something like this:
- (void)showRandomColourAfterDelay {
static NSUInteger count = 0;
switch (arc4random()%3) {
case 0:
labelsText.text = #"Red";
case 1:
labelsText.text = #"Blue";
case 2:
labelsText.text = #"Green";
}
count++;
if (count >= 3) return;
[self performSelector:#selector(showRandomColourAfterDelay) withObject:nil afterDelay:3];
}
In fact, I'd probable use an NSArray to hold the colour strings, but that would involve changes outside of a single method, so I've stuck with your hard-coding approach.

retain with objective-c (again but with more precision)

I post this question again but with more precision this time,
first, I have this function who return a string, there is some error about memory management or this fonction is ok?
-(NSString *) motAvecCle:(NSString *) cle
{
NSString *motRetour;
motRetour = #"";
cle = [FonctionUtile concatener:[[tableauConfig singletonTableauConfig] langueAffichage] chaine2:#"_" chaine3:[FonctionUtile trim:[cle uppercaseString]] chaine4:#""];
motRetour = [FonctionUtile trim:[dictionnaireLangue objectForKey:cle]];
if (motRetour == nil) {
motRetour = #"Erreur";
}
return motRetour;
}
and when I call this fonction,
NSString *myString = #"";
myString = [self motAvecCle:#"fr"]; // I must do this?
myString = [[self motAvecCle:#"en"]retain]; //or do this?
thx again...
The method motAvecCle: returns an object you do not own. Therefore, at some point it is going to disappear. Whether you care or not depends on where myString is defined. If it's in the same scope:
-(void) foo
{
NSString *myString = [self motAvecCle:#"fr"];
// do some stuff
}
you do not want to retain it (except in one circumstance) because the reference will disappear when foo exits which means if you had retained it you'd need to release it again first.
The one circumstance for retaining is if you modify the object you got the string from i.e. self in this case. That might cause the string to go away (although probably not in your specific example).
If myString is an instance variable of the object, you do want to retain it because otherwise it will disappear (possibly) the next time the auto release pool is drained. However, before assigning the instance variable, you must be sure to release the old value of the instance variable, unless it's actually the same string you are assigning i.e. you need to do something like this:
-(void) foo
{
NSString *tmp = [[self motAvecCle:#"fr"] retain]; // it's a string, technically you should copy, not retain
[myString release];
myString = tmp;
// do some stuff
}
Since you'd have to do that every time you want to assign the ivar, it's normal to create an accessor e.g.
-(void) setMyString: (NSString*) newValue
{
NSString* tmp = [newValue copy];
[myString release];
myString = tmp;
}
-(void) foo
{
[self setMyString: [self motAvecCle:#"fr"]];
// do some stuff
}
If you use properties, you can use #synthesize to create the accessors.

Iphone: Passing objects and Multiple threads

Im having some trouble passing an NSNumber object to different threads.
I call a function on viewDidload that loads up some objects from core data as a background process. which calls another function which loops through the loaded objects to see if there are any images associated with it alredy downloaded. if its not present, download the images asynchronously and save it locally. The thing is I need to perform startDownloadFor:atIndex: on the main thread. But the application crashes because of the NSNumber object thats being passed. here is the code..
- (void)viewDidLoad {
...
...
[self performSelectorInBackground:#selector(loadImages) withObject:nil];
}
-(void)loadImages{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
...
...
[self fillInImages];
[pool release];
}
-(void)fillInImages{
NSString *imageURL;
for (int i=0; i < [dataManager.objectList count]; i++) {
...
if ([dataManager.RelatedImages Image] == nil) {
//[self startDownloadFor:imageURL atIndex:[NSNumber numberWithInt:i]; // << WORKS FINE
[self performSelectorOnMainThread:#selector(startDownloadFor:atIndex:) withObject:(imageURL, [NSNumber numberWithInt:i]) waitUntilDone:YES]; // << CRASHES
...
}else {
...
}
...
}
...
}
-(void)startDownloadFor:(NSString*)imageUrl atIndex:(int)indexPath{
NSString *indexKey = [NSString stringWithFormat:#"key%d",indexPath];
...
}
what is the right way of doing this?
Thanks
I've never seen that syntax passing more than one object to a selector - is that valid objective-c code? also, in your startDownloadFor:atIndex: you're passing in an NSNumber but the type for the second parameter on that selector is (int) - that can't be good ;)
The docs for performSelectorOnMainThread: say that the selector should take only one argument of type id. You're passing an invalid selector so I think that it's getting very confused about where the NSNumber is.
To fix it, pass an NSDictionary conatining the number and the image URL i.e.
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:imageURL, #"imageURL", [NSNumber numberWithInt:i], #"number", nil];
[self performSelectorOnMainThread:#selector(startDownload:) withObject:dict waitUntilDone:YES];
and
//-(void)startDownloadFor:(NSString*)imageUrl atIndex:(int)indexPath{
- (void)startdownload:(NSDictionary *)dict {
NSURL *imageURL = [dict objectForKey:#"imageURL"];
int indexPath = [[dict objectforKey:#"number"] intValue];
You are trying to pass 2 arguments into performSelectorOnMainThread:withObject:waitUntilDone: while the method only supports passing one argument.
You need to use NSInvocation to send more arguments (or use an NSDictionary like dean proposed).
SEL theSelector;
NSMethodSignature *aSignature;
NSInvocation *anInvocation;
theSelector = #selector(startDownloadFor:atIndex:);
aSignature = [self instanceMethodSignatureForSelector:theSelector];
anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
[anInvocation setSelector:theSelector];
[anInvocation setTarget:self];
// indexes for arguments start at 2, 0 = self, 1 = _cmd
[anInvocation setArgument:&imageUrl atIndex:2];
[anInvocation setArgument:&i atIndex:3];
[anInvocation performSelectorOnMainThread:#selector(invoke) withObject:NULL waitUntilDone:YES];