I created a common method "isValidAmount" to check if the user inputs a correct amount in UITextField. This is working, but the problem is, I am getting leaks when the number is invalid. The leaked object is "NSCFNumber" and the responsible for that is "getObjectValue". I don't know what else to do. I already release the "formatter" below. I even tried to release "number" below. But still I keep on getting this leak. Please help.
+ (BOOL) isValidAmount:(NSString *)amount {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *number = [formatter numberFromString:amount];
[formatter release];
if (!number) {
return NO;
}
return YES;
}
By inspection, there is nothing wrong with that code.
Does it also leak on the device? Can you post the exact backtrace of the allocation that is leaked?
Related
Can anyone see potential leaks in this code? I'm getting a "100%" leak according to Instruments on the line "NSString *ender = [temp stringFromDate:now];"
NSDateFormatter* temp = [[NSDateFormatter alloc] init];
[temp setDateFormat:#"yyyy-MM-dd"];
NSDate *now = [NSDate date];
NSString *ender = [temp stringFromDate:now];
DashboardViewController *controller = [[DashboardViewController alloc] init];
[controller initWithStartDate:ender andEndDate:ender];
[controller initAccount:account];
[self presentModalViewController:controller animated:NO];
[temp release];
Do you release controller after all that stuff?
This advice is unrelated to original question, but I think you should rename the initWithStartDate:andEndDate: and initAccount: methods since typically methods with "init" in the name return new instances.
Perhaps create your own -(id)initWithStartDate:endDate:account: and call the designated initializer from within.
Then you would create a new controller instance with
DashboardViewController *controller = [[DashboardViewController alloc] initWithStartDate:ender endDate:ender account:account];
Gonzalo
Since you pass your controller instance to the -presentModalViewController: method, that method will retain your controller. So you can safely release your controller, but you also should release your controller, since the memory management rules state that objects that you alloc+inited are owned by you and must be released.
On the other hand - just a small note - NSDateFormatter is a "heavy" object, cache the instance and reuse it, if it's possible. Probably this is also the reason why Apple deprecated this method. You might call -init on NSDateFormatter from iOS 2.0 till iOS 3.2, but it is deprecated after iOS 3.2 .
Allocating NSDateFormatter (or NSNumberFormatter) is relatively slow and
cellForRowAtIndexPath runs for every cell.
So, allocating formatters in cellForRowAtIndexPath can be a significant contributor to jerky scrolling.
To smooth scrolling, I tried allocating them outside cellForRowAtIndexPath,
by making them a class variable and allocating them in viewWillAppear and releasing them in viewWillDisappear (see code below).
But that produce leaks in the formatters.
Where is the best place to declare/allocate/release formatters used in cellForRowAtIndexPath?
//in myNavigationViewController.m:
NSDateFormatter *myDateFormatter;
-...viewWillAppear...{
if(myDateFormatter){ //Solution: add this check.
[myDateFormatter release];
}
myDateFormatter = [[NSDateFormatter alloc] init];
[myDateFormatter setDateStyle:NSDateFormatterShortStyle];
[myDateFormatter setTimeStyle:NSDateFormatterShortStyle];
if(locale){ //Solution: add this check.
[locale release];
}
locale = [NSLocale currentLocale];
[myDateFormatter setLocale:locale];
}
-...cellForRowAtIndexPath... {
cell.myDateLabel.text = [myDateFormatter stringFromDate:_date];
}
-...viewWillDisappear...{
// [myDateFormatter release]; //Solution: remove this line
}
-...dealloc {
[myDateFormatter release]; //Solution: add these 2 lines.
[locale release];
}
It shouldn't leak at all so long as you remember to deallocate it in -dealloc.
I've been having some trouble getting NSNumberFormatter to work properly.
I'm compiling in iOS4 with the Three20 Framework.
All of the NSNumberFormatter selectors require something called NS_AVAILABLE as a second parameter on the selector:
[numberFormatter setCurrencyGroupingSeparator:(NSString *)string __AVAILABILITY_INTERNAL__IPHONE_2:(int)_0];
I'm not sure what I'm supposed to do with the second parameter. I've tried:
[numberFormatter setCurrencyGroupingSeparator:#"," __AVAILABILITY_INTERNAL__IPHONE_2:2]; // Warning: NSNumberFormatter' may not respond to '-setCurrencyGroupingSeparator:__AVAILABILITY_INTERNAL__IPHONE_2:
[numberFormatter setCurrencyGroupingSeparator:#"," __AVAILABILITY_INTERNAL__IPHONE_2:2_0]; //Error: invalid suffix "_0" on integer constant
and several more iterations of that.
If I don't specify the __AVAILABLE_INTERNAL__IPHONE_2 it doesn't throw a warning and compiles fine, but the selector text is black, not dark purple in Xcode as if it's not recognized.
Here's the full code snippet:
NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[numberFormatter setCurrencyGroupingSeparator:#","];
[numberFormatter setUsesGroupingSeparator:YES];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
numberString = [NSString stringWithFormat: #"$%#",[numberFormatter stringFromNumber:myNsNumber]]; // myNsNumber is an NSNumber with a value of 10000000
NSLog(#"numberString: %#",numberString); // numberString: (null) | instead of numberString: $10,000,000
I've Googled _AVAILABILITY_INTERNAL__IPHONE_2 and NS_AVAILABLE with no luck.
Searching developer.apple.com only gives me the iOS 4.0 API Diffs.
Is this an issue with Three20? Does anyone know whats going on here or what NS_AVAILABLE is all about?
Forget about just about everything you have in this post so far. The availability stuff lets you know what SDK the methods are included in.
This is working NSNumberFormatter code.
NSNumber *number = [UINumber numberWithInt:42];
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
[nf setFormatterBehavior:NSNumberFormatterBehavior10_4];
[nf setNumberStyle:NSNumberFormatterDecimalStyle];
NSString *string = [nf stringFromNumber:number];
[nf release];
If you have a null string, then it is either because your NSNumber is null or NaN, or your format is screwed up.
Instruments tells me the following line from the code below is leaking: I can't figure out how to fix this leak.
[self.selectedElement.usrAdvancedBuyingPercents replaceObjectAtIndex:selectedRow withObject:[numberFormatter stringFromNumber:percentage]];
- (IBAction) simpleMarginSliderValueChanged:(UISlider *)sender {
NSDecimalNumber *percentage = (NSDecimalNumber *)[NSDecimalNumber numberWithFloat:[sender value]];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setPositiveFormat:#"#.##"];
[self.selectedElement.usrAdvancedBuyingPercents replaceObjectAtIndex:selectedRow withObject:[numberFormatter stringFromNumber:percentage]];
[numberFormatter release];
}
The NSString you are creating from the number is not being released somewhere.
The problem is not in the code that is shown - it's somewhere else that is taking a string from that array, retaining it, then not releasing it. Leaks just shows you where memory that is leaked was initially allocated, and the only thing on that line that is allocating memory is [numberFormatter stringFromNumber:percentage].
Either that, or the whole array is not being released correctly (but then whatever builds usrAdvancedBuyingPercents would also show that is leaking).
I have a weird memory leak with NSTimeIntervall and NSDate. Here is my code:
NSTimeInterval interval = 60*60*[[[Config alloc] getCacheLifetime] integerValue];
NSDate *maxCacheAge = [[NSDate alloc] initWithTimeIntervalSinceNow:-interval];
if ([date compare:maxCacheAge] == NSOrderedDescending) {
return YES;
} else {
return NO;
}
date is just an NSDate object, this should be fine. Instruments tells me that "interval" leaks, yet I do not quite understand this, how can I release a non-object? The function ends after the code snippet I posted here, so from my understanding interval should get automatically deallocated then.
Thanks a lot!
It is probably telling you that a leak is happening on that line.
The expression [[[Config alloc] getCacheLifetime] integerValue] is your problem.
First of all, you care creating an object (calling alloc) but you lose the reference to it before calling release or autorelease, so it is leaking.
Also, you really ought to call an init method immediately after allocating the object. Even if your Config class doesn't do anything special, NSObject's init method will need to be called.
If you replace that line with
Config *config = [[Config alloc] init];
NSTimeInterval interval = 60*60*[[config getCacheLifetime] integerValue];
[config release];
That leak should be plugged up.
You are also leaking the maxCacheAge object. Inserting [maxCacheAge autorelease]; before the if statement should fix that.
Found the problem, in case you come across the same issue, this is the solution:
[[ClubzoneConfig alloc] loadConfigFile];
NSTimeInterval interval = 60*60*[[[ClubzoneConfig alloc] getCacheLifetime] integerValue];
NSDate *maxCacheAge = [[NSDate alloc] initWithTimeIntervalSinceNow:-interval];
if ([date compare:maxCacheAge] == NSOrderedDescending) {
[maxCacheAge release];
return YES;
} else {
[maxCacheAge release];
return NO;
}
The problem is that the maxCacheAge object needs to get released, as I own it (see link below).
I got it thanks to the awesome solution here: iPhone Memory Management