Over-releasing an NSDecimalNumber - objective-c

My app is telling me that I'm over-releasing the NSDecimalNumber tempDouble below:
NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setLocale:[NSLocale currentLocale]];
[currencyFormatter setGeneratesDecimalNumbers:TRUE];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
// Here is the key: use the maximum fractional digits of the currency as the scale
int currencyScale = [currencyFormatter maximumFractionDigits];
NSDecimalNumber* tempDouble = [[NSDecimalNumber alloc] initWithString:self.tempStore];
NSDecimalNumber* numTen = [[NSDecimalNumber alloc] initWithInt:10];
tempDouble = [tempDouble decimalNumberByDividingBy:[numTen decimalNumberByRaisingToPower:currencyScale]];
[numTen release];
[textField setText:[currencyFormatter stringFromNumber:tempDouble]];
[currencyFormatter release];
[tempDouble release];
I'm thinking that the problem line is this one:
tempDouble = [tempDouble decimalNumberByDividingBy:[numTen decimalNumberByRaisingToPower:currencyScale]];
But I'm not sure why. Should I be adding an 'assign, copy or retain' attribute after the assignment? When I get rid of the 'release' statement below, the code works fine.
Thanks,
G

The problem is indeed in this line:
tempDouble = [tempDouble decimalNumberByDividingBy:[numTen decimalNumberByRaisingToPower:currencyScale]];
What you're doing here is replacing the previous value in tempDouble (which has a retain count of 1) with a new value (which is autoreleased).
Here's the correct way to do it:
NSDecimalNumber* tempDouble = [[[NSDecimalNumber alloc] initWithString:self.tempStore] autorelease];
And then delete the [tempDouble release] from the end of the method.

Related

NSNumberFormatter: dispatch_once where Currency changes

I have a tableview where a list of NSDecimalNumber will be displayed with their own currencies.
I use a dispatch_once on a NSNumberFormatter but notice that sometimes it'll totally ignore my commands to set the fraction digits or suffix on fraction digits altogether.
Currently the only way I've been able to resolve this is to init the NSNumberFormatter every row which is a bit silly.
ie:
- (NSNumberFormatter *)currencyFormatWithCurrency:(NSString *)currency
{
static NSNumberFormatter *_formatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_formatter = [[NSNumberFormatter alloc] init];
});
[_formatter setCurrencyCode:currency];
[_formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[_formatter setMaximumFractionDigits:0];
[_formatter setGeneratesDecimalNumbers:NO];
[_formatter setRoundingMode:NSNumberFormatterRoundUp];
return _formatter;
}
// someCurrency is a string that will change per row
// someAmount is a NSDecimalNumber that will change per row
NSNumberFormatter *formatter = [self currencyFormatWithCurrency:someCurrency];
NSString *formattedAmount = [formatter stringFromNumber:someAmount];
self.amountLabel.text = formattedAmount;
Sometimes with the above if I feed in a figure like 80000, it'll return 80,000.00 (sometimes it depends on the currency).
I guess I could just init the NSNumberFormatter and nil it on each row.
Perhaps I'm doing it wrong, but if there is perhaps a way to ensure that I don't need to keep creating the NSNumberFormatter to ensure it'll understand the rules I give it?
Many thanks
NSNumberFormatter is not thread safe. That will lead to all kinds of problems. (Well, the way you use it cannot really be thread safe, because you might be modifying it in one thread while another thread is using it, but just using an NSNumberFormatter from two threads is not safe).
You can always do something in a method like
NSString* lastCurrency = nil;
NSNumberFormatter* formatter = nil;
for (;;) {
...
NSString* currency = ...;
if (! [lastCurrency isEqualToString:currency]) {
lastCurrency = currency;
formatter = ...;
}
}
Works well if in most cases the currency is unchanged.
I think this is a possible solution which I got from the following website might help things.
NSString *HTCurrencyString(double amount, NSString *currencyCode)
{
static NSString *currencyFormatterKey = #"HTCurrencyFormatter";
NSNumberFormatter *currencyFormatter = [[NSThread currentThread] threadDictionary][currencyFormatterKey];
if (currencyFormatter == nil)
{
currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[currencyFormatter setLocale:[NSLocale currentLocale]];
[[NSThread currentThread] threadDictionary][currencyFormatterKey] = currencyFormatter;
}
[currencyFormatter setCurrencyCode:currencyCode];
[currencyFormatter setMaximumFractionDigits:0];
[currencyFormatter setRoundingMode:NSNumberFormatterRoundHalfUp];
return [currencyFormatter stringFromNumber:#(amount)];
}

Convert a string to a positive or negative number

I am new to ObjC. I've spent years working in Applescript and I've decided to move up. I am a hobbiest programmer.
I have the following code:
+(NSArray *) initArrayWithFileContents:(NSString *) theFilePath
{
NSString *theContents = [(self) loadFile:theFilePath]; // returns the contents of a text file
NSArray *theParagraphs = [(self) getParagraphs:theContents]; // returns the contents as an array of paragraphs
NSMutableArray *teamData = [[NSMutableArray alloc] init]; // array of team data
NSMutableArray *leagueData = [[NSMutableArray alloc] init]; // array of arrays
NSNumberFormatter *numberStyle = [[NSNumberFormatter alloc] init];
[numberStyle setNumberStyle:NSNumberFormatterDecimalStyle];
for (NSString *currentParagraph in theParagraphs)
{
NSArray *currentTeam = [(self) getcolumnarData:currentParagraph];
for (NSString *currentItem in currentTeam)
{
NSNumber *currentStat = [numberStyle numberFromString:currentItem];
if (currentStat != Nil) {
[teamData addObject:currentStat];
} else {
[teamData addObject:currentItem];
}
}
[leagueData addObject:teamData];
[teamData removeAllObjects];
}
return leagueData;
}
This works fine for strings and for negative numbers, but a number preceded by a "+" sign is returned as a string. I figure I need to use a different number formatter style but I don't know what to use.
Thanks in advance,
Brad
NSNumberFormatter *numberStyle = [[NSNumberFormatter alloc] init];
[numberStyle setNumberStyle:NSNumberFormatterDecimalStyle];
[numberStyle setPositiveFormat:#"'+'#"] ;
or
NSNumberFormatter *numberStyle = [[NSNumberFormatter alloc] init];
[numberStyle setNumberStyle:NSNumberFormatterDecimalStyle];
[numberStyle setPositivePrefix:#"+"] ;
You could remove the + sign if one exists:
if ([currentItem hasPrefix:#"+"])
{
currentItem = [currentItem substringWithRange:NSMakeRange(1, [currentItem length] -1)];
}
Probably a better way, but this would work.
What ended up working for me was to create 2 NSNumberFormatters; 1 for decimals and 1 for decimals using the setPositiveFormat: method as described above. If the first formatter doesn't work, it'll flow on to the next formatter using the positive format.

Getting nil from NSNumberformater numberFromString

I'm trying to format an amount from a .txt file coming in es_US locale(x,xxx.xx), to my current locale with is es_ES(x.xxx,xx). I would expect that [NSNumberFormater numberFromString] would just reformat the string, however and I'm only getting a nil value from this method.
I also tried another approach after checking the answers from here, but NSDecimalnumber does not work if the string has thousand separators, so if anybody could tell me what am I doing wrong please...
- (void) setSaldo_sap:(NSString *)saldo_sap
{
NSNumberFormatter *numFormatter = [[NSNumberFormatter alloc] init];
[numFormatter setLocale:[NSLocale currentLocale]];
[numFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[numFormatter setNegativeFormat:#"-¤#,##0.00"];
//saldo_sap = #" -324,234.55"
NSString * tmpString = [saldo_sap stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSNumber *num = [numFormatter numberFromString:tmpString];
NSDecimalNumber *tempNumber = [NSDecimalNumber decimalNumberWithString:tmpString];
_saldo_sap = [numFormatter stringFromNumber:tempNumber];
}
I think you misinterpret the aim of NSNumberFormatter: it doesn't "reformat", it "formats" and "parses" a numbers formatted along the set rules. So if you have numbers coming in "es_US" locale but want to format them using "es_ES" you will need two NSNumberFormatters: one for each locale.
Parse the incoming number with "es_US" and format using "es_ES", simplifying a bit (I don't know those two locales and the exact format of your numbers so you may need to tweek it a bit):
NSString * tmpString = ...
NSNumberFormatter *usFormatter = [[NSNumberFormatter alloc] init];
[usFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier: #"es_US"] autorelease]];
[usFormatter setHasThousandSeparators: YES];
NSNumberFormatter *esFormatter = [[NSNumberFormatter alloc] init];
[esFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier: #"es_ES"] autorelease]];
[esFormatter setHasThousandSeparators: YES];
// this assignment looks also somewhat weird: is it an instance variable?
// 'cause if it is and you assign an autoreleased string you'll have a bad pointer there
_saldo_sap = [esFormatter stringFromNumber: [usFormatter numberFromString: tmpString]];
// And unless you use ARC you leak your formatter on each call, so at the end
[usFormatter release];
[esFormatter release];
EDIT
If the input strings contain prefix/postfix characters, that may prevent NSNumberFormatter to work (it use usually pretty strict), use setLenient::
"Sets whether the receiver will use heuristics to guess at the number which is intended by a string."
If you have more than one number to be converted, do not create the formatters for each number, this is just a waste of memory and cpu. Make them instance variables and reuse. It will be much clearer than just having one formatter and reconfiguring it between parsing one format and formatting in another.
NSString *_saldo_sap = #" -324234.55";
//NSString *_saldo_sap = #" 324,234.55";
NSString * tmpString = [_saldo_sap stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
//tmpString = #"-324,234.55"
NSNumberFormatter *numFormatter = [[NSNumberFormatter alloc] init];
NSNumber *num = [numFormatter numberFromString:tmpString];
[numFormatter setLocale:[NSLocale currentLocale]];
[numFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numFormatter setNegativeFormat:#"-¤#,##0.00"];
_saldo_sap = [numFormatter stringFromNumber:num];
Firstly, for getting NSNumber from NSString the string must be in correct readable format i.e. it must not include any characters like " , " as stated.
Secondly, you must first convert the string to NSNumber and then format it accordingly.

Releasing or Autoreleasing objects

I have a problem understanding this code:
- (void)subnetMaskByNumberOfSubnetBits:(id)sender{
// ------- Sets the subnet mask when the user selects the number of bits
NSNumberFormatter *stringToNumber = [[NSNumberFormatter alloc] init];//TURN A STRING INTO A NUMBER
NSNumber *selectedAmountOfBits = [[NSNumber alloc] init];//CONTAINS THE SELECTED NUMBER OF BITS
selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
[self changeSubnetMaskUsingNumberOfMaskBits:selectedAmountOfBits];
//RELEASE
[stringToNumber release];
[selectedAmountOfBits release];
}
I kept getting errors because of the fact that I released selectedAmountOfBits.
I initialized the object using alloc and init.
Why don't I need to release it?
The problem is that you are assigning an object to selectedAmountOfBits twice.
NSNumber *selectedAmountOfBits = [[NSNumber alloc] init];
allocates a new NSNumber object that you own and assigns it to selectedAmountOfBits.
selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
assigns a new autoreleased object to selectedAmountOfBits. This means that when you do [selectedAmountOfBits release], you are actually trying to release an object that you do not own. You are also leaking the original NSNumber that you created since you've lost any reference to it.
The solution is to remove the alloc/init line, keep the autoreleased NSNumber, and get rid of the line where you release it. The final code should look like this:
- (void)subnetMaskByNumberOfSubnetBits:(id)sender{
// ------- Sets the subnet mask when the user selects the number of bits
NSNumberFormatter *stringToNumber = [[NSNumberFormatter alloc] init];//TURN A STRING INTO A NUMBER
NSNumber *selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
[self changeSubnetMaskUsingNumberOfMaskBits:selectedAmountOfBits];
//RELEASE
[stringToNumber release];
}
There are some issues in the original code, I added //!i comments below:
- (void)subnetMaskByNumberOfSubnetBits:(id)sender{
NSNumberFormatter *stringToNumber = [[NSNumberFormatter alloc] init];
//!i: The [[NSNumber alloc] init] is unnecessary. You are creating a pointer to a dummy number
// that is immediately overwritten in the next line
NSNumber *selectedAmountOfBits = [[NSNumber alloc] init];
//!i: At this point, you overwrite the pointer stored in selectedAmountOfBits to point to a new
// NSNumber, returned by numberFromString:, and in the autorelease pool
selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
//!i: you are now leaking the number allocated via [[NSNumber alloc] init], as you no longer have
// a variable tracking the pointer to it
[self changeSubnetMaskUsingNumberOfMaskBits:selectedAmountOfBits];
[stringToNumber release];
//!i: You are calling -release on the number that is in the autorelease pool, not on the
// original number you allocated via [[NSNumber alloc] init]
[selectedAmountOfBits release];
}
You can fix this as follows:
- (void)subnetMaskByNumberOfSubnetBits:(id)sender{
NSNumberFormatter *stringToNumber = [[NSNumberFormatter alloc] init];
NSNumber *selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
[self changeSubnetMaskUsingNumberOfMaskBits:selectedAmountOfBits];
//!i: You still need the -release here, as stringToNumber points to the
// NSNumberFormatter that you created using alloc/init
[stringToNumber release];
}

Need Help Rounding Numbers (Float)

In the following code, I want the the number that satisfies if (isKgs) to be rounded to one decimal point.
For example right now it is giving me 2.2643534543 but I just want 2.3.
Any ideas?
NSNumber *weightInPounds = [self.pickerArray objectAtIndex:row];
NSNumber *weightInKilos = [[DDUnitConverter massUnitConverter] convertNumber: weightInPounds fromUnit: DDMassUnitUSPounds toUnit: DDMassUnitKilograms];
NSString *temp = [NSString stringWithFormat:#"%# kgs", [weightInKilos stringValue]];
[self.firstComponentText setString:temp];
The the type of number from the picker is float, I believe.
You can format the NSNumber object using an NSNumberFormatter object. An example,
NSNumber * decimal = [NSNumber numberWithFloat:2.2643534543];
NSNumberFormatter * formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setRoundingMode:NSNumberFormatterRoundHalfUp];
[formatter setMaximumFractionDigits:1];
NSLog(#"%#", [formatter stringFromNumber:decimal]);