What is causing the leaks in this code? I really cannot understand it.
On thes lines:
1: NSMutableArray * days = [[NSMutableArray alloc]init];
2: [dic setObject:days forKey:key];
3: [days addObject:value];
The whole method:
-(void) addValueToDictionary: (NSMutableDictionary *) dic withValue: (NSNumber *) value forKey: (NSString *) key {
NSMutableArray * days = [dic objectForKey:key];
if (days == nil) {
NSMutableArray * days = [[NSMutableArray alloc]init];
[days addObject:value];
[dic setObject:days forKey:key];
[days release];
days = nil;
}
else {
[days addObject:value];
}
}
BR
//Christoffer
Check to make sure that dic is released. You should NSLog retainCount before where you think the final releases are and make sure they are 1 right before the final release.
Also, run a Build and Analyze to make sure you are releasing correctly. The built in Build and Analyze doesn't find as many leaks as running scan-build with all checks, so look into installing scan-build into Xcode as well.
Using an external Xcode Clang Static Analyzer binary, with additional checks
You should be getting a warning about re-declaring days. This may be throwing the leak check off if you are using the static analyser. Modified method below. Mostly coding style changes with a little defensive coding added.
-(void) addValueToDictionary: (NSMutableDictionary *) dic withValue: (NSNumber *) value forKey: (NSString *) key
{
if (nil == dic || nil == key || nil == value) return; // bail out on nil parameters
if (![dic objectForKey:key]) {
NSMutableArray * days = [[NSMutableArray alloc] init];
[dic setObject:days forKey:key];
[days release];
}
[[dic objectForKey:key] addObject:value];
}
Have you tried to change the name of the variable NSMutableArray *days within the if? Don't you get a warning because of that?
There's nothing wrong with that particular piece of code (other than the slightly questionable redefinition of days in the inner scope). Somewhere else you are retaining but forgetting to release the object you put in the dictionary.
change NSMutableArray initialization to...
NSMutableArray * days = [NSMutableArray array];
Related
I am using XCode for developing an iPhone app. I am new to this platform and need some help with a particular issue...
I have a method that processes some data and returns two integer values as NSNumber wrapped into a NSMutableArray.
Here is the method:
-(NSMutableArray *)processPoints:(int) x:(int) y
{
NSMutableArray *mutArray = [[NSMutableArray alloc] initWithCapacity:3];
int x9,y9;
// ...do some processing...
NSNumber* xNum = [NSNumber numberWithInt:x9];
NSNumber* yNum = [NSNumber numberWithInt:y9];
[mutArray addObject:xNum];
[mutArray addObject:yNum];
return [mutArray autorelease];
}
I call the above method from another method, where I copy the NSNumber stuff into local variables and then release the local copy of NSMutable array.
But the app crashes when releasing this NSMutable array (variable 'mutArray').
Here is the method:
-(void)doNinjaAction
{
NSMutableArray* mutArray = [self processPoints: x :y];
NSNumber* s1 = [[mutArray objectAtIndex:0] retain];
NSNumber* s2 = [[mutArray objectAtIndex:1] retain];
x = [s1 integerValue];
y = [s2 integerValue];
//...proceed with other stuff...
[mutArray autorelease]; //this is where the system crashes. same for 'release'
//instead of 'autorelease'
}
Can you please explain where I am going wrong with the process of memory release.
My understanding of the process is a bit shaky. Please help.
Because you're overreleasing the array. You alloc-init it in processPoints:, then you autorelease it - that's correct, this is how you dispose of its ownership.
After that, you don't need to and must not autorelease or release it once again. This is not malloc() from the standard library.
when you call the statement
NSMutableArray* mutArray = [self processPoints: x :y];
This itself acts as autorelease.
Hence releasing the array explicitly will cause the app to crash.
You are releasing mutArray more then once. Once in processPoints function and again in doNinjaAction.
To resolve the crash remove :
[mutArray autorelease];
-(NSMutableArray *)processPoints:(int) x:(int) y
{
NSMutableArray *mutArray = [[NSMutableArray alloc] initWithCapacity:3];
int x9,y9;
// ...do some processing...
NSNumber* xNum = [NSNumber numberWithInt:x9];
NSNumber* yNum = [NSNumber numberWithInt:y9];
[mutArray addObject:xNum];
[mutArray addObject:yNum];
[mutArray autorelase];
return mutArray;
}
try this one it'll resolve it.
-(NSMutableArray *)processPoints:(int) x:(int) y
{
NSMutableArray *mutArray =[[[NSMutableArray alloc] initWithCapacity:3]autorelease];
int x9,y9;
// ...do some processing...
NSNumber* xNum = [NSNumber numberWithInt:x9];
NSNumber* yNum = [NSNumber numberWithInt:y9];
[mutArray addObject:xNum];
[mutArray addObject:yNum];
return mutArray;
}
As #H2CO3 and #AppleDelegate suggested, it is right.
Still I would suggest to use ARC and convert your project to ARC enabled.
Go to Edit->Refactor->Convert to Objectiv-C ARC
Then you dont need to do any releases anywhere. It will take care itself of all the releases :)
I hate asking memory management questions - all the good answers
are variations on RTFM. But this one is stumping me.
I have a (relatively) complex dictionary in my model class,
where each key points to an array of arrays. I constantly add and delete
items to it, depending on state. Each "item" is an array.
- (void)addToDictionary:(NSNumber *)itemID {
// get what we need (associated array of arrays & key) from the incoming ID
NSArray *incomingArray = [self getArrayFromID:[itemID intValue]];
NSString *myKey = [incomingArray objectAtIndex:0];
NSMutableArray *myNewArray = [[NSMutableArray alloc] init];
// case 1: this key is not in the dictionary yet
if ([[myDict allKeys] containsObject:myKey] == NO) {
[myNewArray addObject:incomingArray];
[myDict setObject:myNewArray forKey:myKey];
// case 2: key already there; add new array to its array
} else {
myNewArray = [NSMutableArray arrayWithArray:[myDict objectForKey:myKey]];
[myNewArray addObject:incomingArray];
[myDict removeObjectForKey:myKey];
[myDict setObject:myNewArray forKey:myKey];
}
// why isn't this line working??
[myNewArray release];
}
My question is the last line. I allocated this array to help me
work with the dictionary, and now I don't need it any more.
But the program will crash if I release it, and work just fine
if I comment that line out. What am I missing?
TIA
In case two you don't own the returned array. So only release it in case 1. And don't create something you'll not use. The NSMutableArray pointer will get assigned to some other data in case 2, not the one you've allocated. So you can't release something you don't own.
- (void)addToDictionary:(NSNumber *)itemID {
NSArray *incomingArray = [self getArrayFromID:[itemID intValue]];
NSString *myKey = [incomingArray objectAtIndex:0];
NSMutableArray *myNewArray;
if ([[myDict allKeys] containsObject:myKey] == NO) {
// Create when you need it
myNewArray = [[NSMutableArray alloc] init];
[myNewArray addObject:incomingArray];
[myDict setObject:myNewArray forKey:myKey];
// release when you're done with it
[myNewArray release];
} else {
myNewArray = [NSMutableArray arrayWithArray:[myDict objectForKey:myKey]]; // you don't own it!
[myNewArray addObject:incomingArray];
[myDict removeObjectForKey:myKey];
[myDict setObject:myNewArray forKey:myKey];
}
// why isn't this line working??
//[myNewArray release];
// because in case 2 it's not pointing to the right memory
}
Hope it works,
ief2
I have got I have got two methods both in different classes. One is class method and other is instance method. i am calling class method from instance method. When instance method finishes it gives runtime error "EXC_BAD_ACCESS".
#import "xmlObject.h"
#import "textmeAppDelegate.h"
#implementation Class1
- (void)method1 {
textmeAppDelegate *del = (textmeAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *bgColor = [[NSArray alloc] initWithArray:[xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]]];
UIColor *color = [UIColor colorWithRed:[[bgColor objectAtIndex:3] floatValue] green:[[bgColor objectAtIndex:2] floatValue] blue:[[bgColor objectAtIndex:1] floatValue] alpha:[[bgColor objectAtIndex:0] floatValue]];
CGContextSetFillColor(context, CGColorGetComponents([color CGColor]));
CGContextFillRect(context, rect);
[bgColor release];
}
#end
#implementation xmlObject
+ (NSArray *) fetchImmediateChildrenValues:(NSMutableDictionary *) node {
NSMutableDictionary *tmp = [[node objectForKey:#"children"] retain];
NSArray *keys = [[NSArray alloc] initWithArray:[tmp allKeys]];
keys = [keys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
NSMutableArray *pushArr = [[[NSMutableArray alloc] init] autorelease];
NSString *val = [[NSString alloc] init];
for(NSString *str in keys) {
val = (NSString *)[[tmp objectForKey:str] objectForKey:#"innertext"];
[pushArr addObject:val];
}
[val release];
[keys release];
return [NSArray arrayWithArray:pushArr];
}
#end
What is wrong with the code? Also app is crashing for this line of code
application is crashing if i include this line
NSArray *bgColor = [[NSArray alloc] initWithArray:[xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]]];
If I remove it application runs smoothly.
I have several comments on your code. One of them is the immediate cause of your crash, but you need to fix at least one other issue too. The short answer is that you over release val and keys.
NSArray *bgColor = [[NSArray alloc] initWithArray:[xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]]];
You don't need to create a new array here, you can simply write the following:
NSArray *bgColor = [xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]];
if you do, you don't need the [bgColor release] further down.
NSArray *keys = [[NSArray alloc] initWithArray:[tmp allKeys]];
keys = [keys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
These two lines leak the first NSArray, you alloc it but you overwrite it straight away with the sorted version. In fact, you can simply write:
keys = [[tmp allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
Note that you do not own keys so you can get rid of the [keys release] line further down.
NSString *val = [[NSString alloc] init];
for(NSString *str in keys) {
val = (NSString *)[[tmp objectForKey:str] objectForKey:#"innertext"];
[pushArr addObject:val];
}
[val release];
This is the source of your immediate problem. You first alloc a new string. Then you immediately overwrite it on each iteration of your loop. So the allocated NSString leaks. You do not own the val returned by [[tmp objectForKey:str] objectForKey:#"innertext"]; on each iteration, so the release ov val after the loop should not be there.
On a side note, objectForKey: returns an id - the cast to NSString* is redundant. Most people leave it out.
[keys release];
Going back to the bit above where I told you that you were leaking your alloc'd keys? Well the new version of keys you overwrote it with you don't own. Therefore you must not release keys here.
return [NSArray arrayWithArray:pushArr];
This is fine. My preference would be for:
return [[pushArray copy] autorelease];
but it is just a matter of style. You could also just return pushArray, but pushArray is mutable and the caller may rely on the return value being immutable.
Test your code with NSZombieEnabled set... It should give you enough informations to fix your problem.
I'm working on implementing a customized searchBar for a fairly complex table and have come across this code pattern AGAIN. This is a sample from the Beginning iPhone Development book:
- (void)handleSearchForTerm:(NSString *)searchTerm
{
NSMutableArray *sectionsToRemove = [[NSMutableArray alloc] init];
[self resetSearch];
for (NSString *key in self.keys)
{
NSMutableArray *array = [self.names valueForKey:key];
NSMutableArray *toRemove = [[NSMutableArray alloc] init];
for (NSString *name in array)
{
if ([name rangeOfString:searchTerm
options:NSCaseInsensitiveSearch].location == NSNotFound)
[toRemove addObject:name];
}
if ([array count] == [toRemove count])
[sectionsToRemove addObject:key];
[array removeObjectsInArray:toRemove];
[toRemove release];
}
[self.keys removeObjectsInArray:sectionsToRemove];
[sectionsToRemove release];
[table reloadData];
}
The part I'm curious about is the "for (NSString *name in array)" section. What is this doing exactly? It seems to create a string for every item in the array. Also, how does this work with dictionaries?
Thanks!
This construct is a different kind of for loop that runs over items in an Objective-C collection, rather than a C array. The first part defines an object that is being set to one element in the collection each run of the loop, while the second part is the collection to enumerate. For example, the code:
NSArray *array = [NSArray arrayWithObjects:#"foo", #"bar", nil];
for(NSString *string in array) {
NSLog(string);
}
would print:
foo
bar
It's defining an NSString *string that, each run of the loop, gets set to the next object in the NSArray *array.
Similarly, you can use enumeration with instances of NSSet (where the order of objects aren't defined) and NSDictionary (where it will enumerate over keys stored in the dictionary - you can enumerate over the values by enumerating over keys, then calling valueForKey: on the dictionary using that key).
It's extremely similar to the construct in C:
int array[2] = { 0, 1 };
for(int i = 0; i < 2; i++) {
printf("%d\n", array[i]);
}
which prints:
0
1
It's just a syntactical way of making the code more readable and hiding some of the fancy enumeration that goes into listing objects in an NSArray, NSSet, or NSDictionary. More detail is given in the Fast Enumeration section of The Objective-C 2.0 Programming Language document.
This is called fast enumeration. It loops through the array, setting key to each item. It's the same, functionally, as doing this:
NSString *key;
for ( NSInteger i = 0; i < [[ self keys ] count ]; i++ ) {
key = [[ self keys ] objectAtIndex:i ];
NSMutableArray *array = [self.names valueForKey:key];
NSMutableArray *toRemove = [[NSMutableArray alloc] init];
for (NSString *name in array)
{
if ([name rangeOfString:searchTerm
options:NSCaseInsensitiveSearch].location == NSNotFound)
[toRemove addObject:name];
}
if ([array count] == [toRemove count])
[sectionsToRemove addObject:key];
[array removeObjectsInArray:toRemove];
[toRemove release];
}
It's a for loop with one iteration for each key in the dictionary.
The for..in construct is called Fast enumeration. You can read more about it in Objective-C 2.0 Programming Guide.
How it works with an object depends on it's implementation of the NSFastEnumeration protocol. The NSDictionary class reference describes how it works with dictionaries:
On Mac OS X v10.5 and later, NSDictionary supports the NSFastEnumeration protocol. You can use the for…in construct to enumerate the keys of a dictionary, as illustrated in the following example.
I'm a beginner at C, Obj-C and the iPhone, and I'm trying getting to grips with a lot of terminology used. I hope one of ye can help with a problem I have been struggling with for a few days now.
My code below is a method which call up a nib containing a search field and a table. The table is populated from a search of the array created for 'theList' below. Using 'Instruments', I am getting a Leak at the line:
NSDictionary *theItem = [NSDictionary dictionaryWithObjectsAndKeys:clientName,#"Name",clientId,#"Id",nil]; , but I can't figure out why :(
I know it's probably a difficult question to answer, but if any one can be of any help!
- (void)editClient:(id)sender {
if (pickList == nil) {
pickList = [[PickFromListViewController alloc] initWithNibName:#"PickList" bundle:nil];
}
TimeLogAppDelegate *appDelegate = (TimeLogAppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray *theList = [[NSMutableArray alloc] init];
int i;
for (i=0;i < [appDelegate.clients count];i++) {
Client *thisClient = [appDelegate.clients objectAtIndex:i];
NSString *clientName = [[NSString alloc] initWithString: thisClient.clientsName];
NSNumber *clientId = [[NSNumber alloc] init];
clientId = [NSNumber numberWithInt:thisClient.clientsId];
NSDictionary *theItem = [NSDictionary dictionaryWithObjectsAndKeys:clientName,#"Name",clientId,#"Id",nil];
[theList addObject:theItem];
theItem = nil;
[clientName release];
[clientId release];
}
[pickList createSearchItems:theList :NSLocalizedString(#"Client",nil)];
[theList release];
appDelegate.returningID = [NSNumber numberWithInt: projectsClientsId];
[self.navigationController pushViewController:pickList animated:YES];
}
Thanks in advance!
This returns allocated NSNumber instance.
NSNumber *clientId = [[NSNumber alloc] init];
This line overwrites the above clientId with another instance of NSNumber, numberWithInt returns autoreleased object, since you haven't allocated memory for it you should not call release, it will be released automatically.
clientId = [NSNumber numberWithInt:thisClient.clientsId];
You are calling release on clientId so you get memory problem.
To fix it remove the first line above which is useless in this case and update the second one to:
NSNumber * clientId = [NSNumber numberWithInt:thisClient.clientsId];
Then remove the:
[clientId release]
Because the clientId will be released automatically.
EDIT: Re still have problems ...
I'm not sure how to you manipulate the clients in app delegate, otherwise the code should work ok, I created small example, omitting the parts that I can't see (app delegate and clients):
// command line utility - foundation tool project:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableArray * theList = [[NSMutableArray alloc] init];
int i = 0;
for (i = 0; i < 10; ++i)
{
NSString * clientName = [NSString stringWithString:#"client"]; //no need to release
NSNumber * clientId = [NSNumber numberWithInt:i];
NSDictionary * theItem = [NSDictionary dictionaryWithObjectsAndKeys:
clientName, #"name",
clientId, #"id",
nil];
[theList addObject:theItem];
}
for (id item in theList) for (id key in item) NSLog(#"%# - %#", key, [item objectForKey:key]);
[theList release];
[pool drain];
return 0;
}
You are creating clientID with [[NSNumber alloc] init], and then immediately overwriting it with an autoreleased NSNumber instance [NSNumber numberWithInt], and then you are releasing it later in your code, which you shouldn't do. Get rid of the [[NSNumber alloc] init] line and the [clientId release] line and that should fix it up a little.
Aside from the obvious leak of the NSNumber, there are a few other things I'd fix that may help. Most are fairly minor, but in my experience with Objective-C, less code == clearer code, something that is not equally true for languages like Bash or Perl. ;-)
- (void) editClient:(id)sender {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (pickList == nil) {
pickList = [[PickFromListViewController alloc] initWithNibName:#"PickList" bundle:nil];
}
TimeLogAppDelegate *appDelegate = (TimeLogAppDelegate*)[[UIApplication sharedApplication] delegate];
NSMutableArray *searchItems = [NSMutableArray array];
NSMutableDictionary *itemDict = [NSMutableDictionary dictionary];
for (Client *client in appDelegate.clients) {
[itemDict setObject:[client.clientsName copy] forKey:#"Name"];
[itemDict setObject:[NSNumber numberWithInt:client.clientsId] forKey:#"Id"];
[searchItems addObject:[[itemDict copy] autorelease]];
}
[pickList createSearchItems:searchItems :NSLocalizedString(#"Client",nil)];
[self.navigationController pushViewController:pickList animated:YES];
appDelegate.returningID = [NSNumber numberWithInt: projectsClientsId];
[pool drain];
}
There are a few mysterious points that make me suspicious:
The line just after the for loop tells pickList to do something with the NSMutableArray. That method should retain the new array, as well as release the old array if one exists. If you just overwrite the pointer, the old array will be leaked. (Also, this method is poorly named. Anonymous arguments (a colon with no preceding text) are legal in Objective-C, but considered extremely bad practice. Consider renaming the method to better express what it does.)
The next line seems to associate the pick list with a navigation controller. If it is custom code, make sure the -pushViewController:animated: method properly releases an existing pick list when a new one is specified.
Assigning to appDelegate.returningID is assumed to call the setter for a returningID property. Be sure that property retains or copies the NSNumber as necessary.
Memory leaks can be tricky to track down, even in Instruments, and you'll often find that it looks like Foundation classes (such as NSDictionary) are leaking like a sieve, but I have always been able to trace it back to an abnormality in my code. :-)