Hi guys,
I've got an object that takes an expression like "23.67*$MY_KEY" and a dictionary with the values mapping to the keys used in the expression. I pipe the key values into a NSPredicate and cast that to a NSExpression to evaluate the expression.
Code:
#import "ExpressionEval.h"
@implementation ExpressionEval
// Evaluates a math expression by substituting in values from a dictionary into known variables
+ (NSDecimalNumber *) calcExpresssion:(NSString *)expr variables:(NSDictionary *)values error:(NSError **)err {
NSDecimalNumber *result;
NSNumber *tempNum;
NSMutableDictionary *substituteValues = [NSMutableDictionary dictionary];
// Cannot process if there is no expression
if (!expr) {
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
[errorDetail setValue:@"Tried to calculate an expression that is nil" forKey:NSLocalizedDescriptionKey];
*err = [NSError errorWithDomain:@"myDomain" code:100 userInfo:errorDetail];
return nil;
}
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
// Covert string values in dictionary with NSNumber instances
for (NSString *key in values) {
id value = [values objectForKey:key];
if ([value isKindOfClass:[NSString class]]) {
// If not a number nil will be returned, add to variable/value dictionary
tempNum = [formatter numberFromString:value];
if (tempNum != nil) {
[substituteValues setValue:tempNum forKey:key];
}
}
}
[formatter release], formatter = nil;
// Add a dummy right hand side to the equation if non exists
NSMutableString *expression = [NSMutableString stringWithString:expr];
NSRange range = [expr rangeOfString:@"=="];
if (range.location == NSNotFound) {
[expression appendFormat:@" == 0"];
}
@try {
// Substite in variable values in expression
NSPredicate *predicate = [NSPredicate predicateWithFormat:expression];
predicate = [predicate predicateWithSubstitutionVariables:substituteValues];
// Evaluate the left hand side of the expression
NSExpression *algExpression = [(NSExpression *)predicate leftExpression];
result = [algExpression expressionValueWithObject:nil context:nil];
}
@catch (id exception) {
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
[errorDetail setValue:@"Tried to calculate an expression that is nil" forKey:NSLocalizedDescriptionKey];
*err = [NSError errorWithDomain:@"myDomain" code:100 userInfo:errorDetail];
return nil;
}
return result;
}
@end
On running this with the Leaks performance tool Im getting the following leaks:
Code:
Leaked Object # Address Size Responsible Library Responsible Frame
NSCFNumber 21 < multiple > 336 Foundation getObjectValue
NSCFNumber 21 < multiple > 336 Foundation getObjectValue
Im suspecting the NSMutableDictionary loaded with NSNumber instances. I have no idea what could be the problem here as I don't know the mechanics of the NSPredicate and NSExpression behind the scenes. Could anyone shine some light on a solution?