NSString, NSArray Memory leak in method - objective-c

everytime I call this method 2 NSString and 1 NSMutableArray objects leak, which is disgusting, because i'm using it a lot in my app.
Here's the method:
+ (NSString *)queryStringFromParameters:(NSDictionary *)parameters {
NSMutableArray __block *entries = [[NSMutableArray alloc] init];
[parameters enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSString *entry = [NSString stringWithFormat:#"%#=%#", [key pcen], [obj pcen]];
[entries addObject:entry];
}];
return [entries componentsJoinedByString:#"&"];
}
Here is the [pcen] method
- (NSString *)pcen {
CFStringRef string = CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)self,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]"),
kCFStringEncodingUTF8);
return [(NSString *)string autorelease];
}
They are in the same file, my project is ARC, but for this file I unchecked ARC.
Why this leak happens every time I try to use it?
Thank you!

You do not release the entries array.
And btw, the __block modifier is not necessary here, because you do not modify that variable inside the block.

You alloc/init the NSArray in the queryStringFromParameters: method. The array you return is indeed an autoreleased object ([entries componentsJoinedByString:#"&"]) but you never release the entries array.
You can replace the line
NSMutableArray __block *entries = [[NSMutableArray alloc] init];
by
NSMutableArray __block *entries = [NSMutableArray array];
to solve the issue.
The strings leak because they are saved in the leaked NSArray.

In your queryStringFromParameters method i think there is no need to alloc entries. Simply use auto-referencing array
NSMutableArray *entries = [NSMutableArray array];
in second method u used CFURLCreateStringByAddingPercentEscapes which has second argument as u passed (CFStringRef)self but that should be OriginalString - The CFString object to copy.
An example of CFURLCreateStringByAddingPercentEscapes is below:
CFStringRef originalURLString = CFSTR("http://online.store.com/storefront/?request=get-document&doi=10.1175%2F1520-0426(2005)014%3C1157:DODADSS%3E2.0.CO%3B2");
CFStringRef preprocessedString = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, originalURLString, CFSTR(""), kCFStringEncodingUTF8);
Also remove __block as #Martin R said

Related

Why is this string immutable?

Why does the code give the error - Attempt to mutate immutable object with appendFormat: ?
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (NSTextTestingResult *match in matches) {
<omitted>
NSMutableString *value;
value = (NSMutableString *)[response stringWithRange:range];
if ([dict objectForKey:#"traveler"])
[dict objectForKey:#"traveler"] appendFormat:#"%#", value]; // Errors here
[dict setObject:value forKey:key];
}
Value is being created as a _NSCFString.
Because [response stringWithRange:range] returns an immutable NSString *, and casting doesn't make it become mutable.
You want value = [[response stringWithRange:range] mutableCopy];.
Note that if you're not using ARC, you need to remember to release the mutableCopy. Although the return value of [response stringWithRange:range] is autoreleased, the mutableCopy is not.
I dont think you can cast a string to mutable like that.
You need to do it like this
ms = [[NSMutableString alloc] init];
[ms setString:immutableString];
Oops wrong again the way the subclass works you should be able to do it like this more simply.
ms = [NSMutableString stringWithString: immutableString];

Seg Fault when using addObject to NSMutableArray

I seem to be having a problem with the NSMutableArray.
Here's my code:
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
while(condition) {
NSInteger temp = someNumber;
[returnArray addObject: temp];
}
But as soon as it hits the addObject message, the program seg faults. Any advice?
You can't add primitives like integers to an array, only objects (hence the name addObject:). If you want to add numbers, you have to convert them to an NSNumber, or one of the related classes.
You can only add objects to array and NSInteger is not an array.
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
while(condition) {
[returnArray addObject: [NSNumber numberWithInt: someNumber]];
}
You need to wrap primitives such as NSInteger into an NSNumber class. You can do the following:
while(condition)
{
NSInteger temp = someNumber;
[returnArray addObject:#(temp)];
}
Or if your compiler doesn't support that syntax:
while(condition)
{
NSInteger temp = someNumber;
[returnArray addObject:[NSNumber numberWithInteger:temp]];
}

Saving int values to Array when app closes

I have an Iphone app that uses alot of int values that will increment on IBActions, I want to save the int values to an array when the app closes so that these values are stores and used when the app is reopened. I am not sure how to write int values into an array. Functionally, I am getting this to wort with text field but not integers i.e.
int count;
code used:
NSArray *values = [[NSArray alloc] initWithObjects:fieldOne.text,fieldTwo.text,count, nil];
This gives an "Assignment makes integer from pointer without cast" message.
Is there anyway to write already stored int values into and array?
code I used is as follows:
I thinks its ok until the calling the data into the array. I have commented out some failed efforts
-(NSString *) saveFilePath{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[path objectAtIndex:0] stringByAppendingPathComponent:#"savefile.plist"];
}
-(void) applicationDidEnterBackground: (UIApplication *) application{
//NSNumber *num = [[NSNumber alloc]initWithInt:count];
NSArray *values = [[NSArray alloc] initWithObjects:fieldOne.text,fieldTwo.text,[NSNumber numberWithInt:count],nil];
[values writeToFile:[self saveFilePath] atomically:YES];
[values release];
}
- (void)viewDidLoad {
//NSNumber *num = [[NSNumber alloc]initWithInt:count];
NSString *myPath = [self saveFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists) {
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
fieldOne.text = [values objectAtIndex:0];
fieldTwo.text = [values objectAtIndex:1];
//[NSNumber count intValue] = [values objectAtIndex:2];
[values release];
}
UIApplication *myApp = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector (applicationDidEnterBackground:)name:UIApplicationDidEnterBackgroundNotification object:myApp];
[super viewDidLoad];
}
Thank you for your help.
You should use NSNumber so something like:
NSNumber *num = [NSNumber numberWithInt:int];
which will give you an object containing your int. To read back from it, use [num intValue]
Create a NSNumber object with your count as its value and put the object into your array. NSArrays need to be arrays of objects.
Container classes work with Objects not fundamental types. You have to wrap fundamental types in NSNumber (numbers) , NSData etc.

I have a memory leak in this objective-c method, can anyone tell me where?

I'm receiving an exc_bad_access somewhere in the code below. I don't understand where it is if anyone could shine any light on it? It's a method that takes in an NSMutableArray of dictionaries and sorts them by one of the elements in the dictionary. The memory leak is almost certainly in the bit with the block but I think i'm missing something fundamental in finding it...
-(NSMutableArray*)sortBicyclesByDistanceToDevice:(NSMutableArray*)inputArray{
NSArray *arrayToHoldSorted = [[[NSArray alloc] init];
arrayToHoldSorted = [inputArray sortedArrayUsingComparator:^(id a, id b){
NSNumber *first = [[a objectForKey:kDistanceFromDevice] objectForKey:kValue];
NSNumber *second = [[b objectForKey:kDistanceFromDevice] objectForKey:kValue];
return [first compare:second];}];
NSMutableArray *retVal = [[NSMutableArray alloc] init];
retVal = [arrayToHoldSorted mutableCopy];
[arrayToHoldSorted release];
return [retVal autorelease];
}
Thanks
It looks like you assign retVal to an NSMutableArray through then reassign immediately after. The original alloced NSMutableArray will leak. That is:
NSMutableArray *retVal = [[NSMutableArray alloc] init];
retVal = [arrayToHoldSorted mutableCopy];
Should be:
NSMutableArray *retVal = [arrayToHoldSorted mutableCopy];
Replace:
NSMutableArray *retVal = [[NSMutableArray alloc] init];
retVal = [arrayToHoldSorted mutableCopy];
With:
NSMutableArray *retVal = [arrayToHoldSorted mutableCopy];
You are leaking the first value of retVal.
There's more than one in there!
This line:
NSArray *arrayToHoldSorted = [[[NSArray alloc] init];
Is a memory leak since you immediately reassign the pointer. It should be removed. Just declare your array on the next line:
NSArray* arrayToHoldSorted = [inputArray sortedArrayUsingComparator...
This method returns an autoreleased object, so you don't need to release it later on.
A similar pattern with the mutable array. You alloc/init, then overwrite with a new object, giving another leak. Again, remove the alloc/init line and just declare in the next line. mutableCopy gives you an implicitly retained object, so you do need to autorelease it.
You seem to be under the impression that alloc/init is needed every time you declare an object variable. This is not the case.
You allocate arrayToHoldSorted (1) - which you never use as you then get an NSArray back from sortedArrayUsingComparator(2). And then you release it afterwards(3) when you don't own it. You do the same trick for retVal, allocating a NSMutableArray - then overwriting your reference to it by getting a new NSMutableArray from [arrayToHoldSorted mutableCopy];
NSArray *arrayToHoldSorted = [[NSArray alloc] init]; .. // 1
arrayToHoldSorted = [inputArray sortedArrayUsingComparator:^(id a, id b) ..... // 2
[arrayToHoldSorted release]; // 3
Just assign the return NSArray from sortedArrayUsingComparator to a reference...
NSArray* arrayToHoldSorted = [inputArray sortedArrayUsingComparator:^(id a, id b) .....
I think the problem is that in this line:
return [retVal autorelease];
you release something that you have not retained. Also in this line:
NSArray *arrayToHoldSorted = [[[NSArray alloc] init];
you have an extra [, which does not help. But most importantly, you can use the static analyzer in XCode to diagnose this sort of bug, rather than pestering the good folk on StackOverflow.

NSArray filled with bool

I am trying to create an NSArray of bool values. How many I do this please?
NSArray *array = [[NSArray alloc] init];
array[0] = YES;
this does not work for me.
Thanks
NSArrays are not c-arrays. You cant access the values of an NSArray with array[foo];
But you can use c type arrays inside objective-C without problems.
The Objective-C approach would be:
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:[NSNumber numberWithBool:YES]];
//or
[array addObject:#(NO)];
...
BOOL b = [[array objectAtIndex:0] boolValue];
....
[array release];
EDIT: New versions of clang, the now standard compiler for objective-c, understand Object subscripting. When you use a new version of clang you will be able to use array[0] = #YES
Seems like you've confused c array with objc NSArray. NSArray is more like a list in Java, into which you can add objects, but not values like NSInteger, BOOL, double etc. If you wish to store such values in an NSArray, you first need to create a mutable array:
NSMutableArray* array = [[NSMutableArray alloc] init];
And then add proper object to it (in this case we'll use NSNumber to store your BOOL value):
[array addObject:[NSNumber numberWithBool:yourBoolValue]];
And that's pretty much it! If you wish to access the bool value, just call:
BOOL yourBoolValue = [[array objectAtIndex:0] boolValue];
Cheers,
Pawel
Use [NSNumber numberWithBool: YES] to get an object you can put in the collection.