I have a big NSDictionary with a smaller NSDictionary inside. I want to autorelease the bigger one, and retain the second. I have this code in my init method:
// Autoreleased stage dictionary
NSString *plistPath = [[NSBundle mainBundle] pathForResource:map ofType:#"plist"];
NSDictionary *mapDict = [NSDictionary dictionaryWithContentsOfFile:plistPath];
// Owned items
citiesDictionary = [[mapDict objectForKey:#"Cities"] retain];
citiesDictionary is declared in the class interface as an:
NSDictionary *citiesDictionary;
If I try to release citiesDictionary in dealloc using the following line it crashes.
[citiesDictionary release];
But if I don't dealloc citiesDictionary, I get a memory leak reported in Instruments when I dealloc the class containing citiesDictionary. I acknowledge that mapDict is being deallocated at the end of init. Does this deallocation affects citiesDictionary even though I called retain on it?
If so, how can I keep the smaller dictionary while freeing the bigger containing one? I tried different things when assigning citiesDictionary, but nothing seems to work correctly. Some of the approaches I tried:
citiesDictionary = [[mapDict objectForKey:#"Cities"] copy];
and
citiesDictionary = [NSDictionary initWithDictionary:[mapDict objectForKey:#"Cities"]];
and even
citiesDictionary = [NSDictionary initWithDictionary:[[mapDict objectForKey:#"Cities"] copy]];
Copy the dictionary to citiesDictionary.
NSString *plistPath = [[NSBundle mainBundle] pathForResource:map ofType:#"plist"];
NSDictionary *mapDict = [NSDictionary dictionaryWithContentsOfFile:plistPath];
citiesDictionary = [[mapDict objectForKey:#"Cities"] copy];
Now you can be sure to have copy of the dictionary even though the containing object can be released at any time. Remember then to also subsequently release the citiesDictionary.
Note when using copy that you will always get an immutable NSDictionary even if the original object was an NSMutableDictionary. Use mutableCopy to obtain an NSMutableDictionary if needed.
You're overreleasing citiesdictionary somewhere, seeing you have already called retain in the init method. Map dict will only release your citiesdictionary once when it is autoreleased, but will not cancel the retain you have done in the init method.
You should try to check your other methods for a release statement to your dictionary, or have the static analyzer tell you.
It was completely my fault: I was inserting a memory-management flawed custom class inside a NSMutableDictionary related to the citiesDictionary, and freeing that other thing was causing the memory to go corrupt.
Now, both
citiesDictionary = [[mapDict objectForKey:#"Cities"] release];
and
citiesDictionary = [[mapDict objectForKey:#"Cities"] copy];
work perfectly fine.
Related
I have a simple method in my model to create a NSDictionary object containing its properties.
Unfortunately this method is seen by "Analyse" to be leaking memory :
Potential memory leak of an object allocated on line 76 (marked here with a dot) and stored in 'dic'.
-(NSDictionary*) getDictionary {
NSDictionary *dic = [[NSDictionary alloc] init];
[dic setValue:(id)self.internal_code forKey:#"internal_code"];
[dic setValue:(id)self.identifier forKey:#"id"];
[dic setValue:(id)self.owner forKey:#"owner"];
[dic setValue:(id)self.address forKey:#"address"];
[dic setValue:(id)self.displayed_name forKey:#"displayed_name"];
return dic;
}
I am not using ARC.
PS : To people coming in, the original code I posted was correct — it had an autorelease. I edited it after so the memory leak would reappear and to ask precisely why.
When returning an object from a method that doesn't begin with alloc, copy, mutableCopy or new, that object must be returned as autoreleased.
More conceptually, it should not be owned by your code when you return it. You take ownership of an object when you type alloc, copy, mutableCopy or new. You relinquish ownership when you type release or autorelease.
You can either change your return statement to:
return [dic autorelease];
Or better is to keep the alloc/init/autorelease all on one line so the code is easier to review, and the alloc and release cannot become separated by accident while copy and pasting code:
NSDictionary *dic = [[[NSDictionary alloc] init] autorelease];
An even easier way is to use this convenience constructor on NSDictionary:
NSDictionary *dic = [NSDictionary dictionary];
The above lines will fix the memory leak. However, you are also trying to mutate an immutable type (NSDictionary). You should be using a mutable dictionary instead:
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
Finally, you should ideally be setting values with the setObject:forKey: method, although setValue:forKey: will also work.
For more information on memory management, read the Advanced Memory Management Programming Guide.
If you are targetting iOS 4 or later, I would highly recommend using ARC.
Try to autorelease while returning the dic as below
return[dic autorelease];
How I must remove objects?
For example, I have NSDictionary and some NSStrings from it:
NSDictionary *dict = [NSDictionary dictionaryWithObjects....];
NSString *str = [dict objectForKey:#"key"];
[str release]; or [str dealloc]; or str = nil or it's autorelease object?
If I will remove not autorelease dict, will all child removed too?
Memory management in objective c is based on object ownership. If you own the object you must release that object.
Cocoa sets the following policy:
You own any object you create
You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).
You can take ownership of an object using retain.
When you no longer need it, you must relinquish ownership of an object you own
You relinquish ownership of an object by sending it a release message or an autorelease message.
You must not relinquish ownership of an object you do not own
This is just corollary of the previous policy rules, stated explicitly.
In your case
NSDictionary *dict = [NSDictionary dictionaryWithObjects....];
NSString *str = [dict objectForKey:#"key"];
you are not owner of str, so you should not release that object.
to remove specific objects you will have to make a mutable Object.
Most of the objective-c dat types are mutable such as
NSMutableDictionary,NSMutableArray..etc
then just call ..[yourObject removeObjectatIndex:someInteger] or [yourObject RemoveAllObjects] to remove objects.
Releasing or auto releasing an object is totally different thing..it will free up the memory that whole object(Dict..array..etc) is taking..
If the call does not end in retain, copy or mutableCopy, alloc or new you must not use release or autorelease. The object is already autoreleased.
NSString *str = [[NSString alloc] initWithString:#"123"]; <- needs release
NSString *str = [aString copy]; <- release
NSString *str = [aString mutableCopy]; <- release
NSString *str = [aString retain]; <- release
NSString *str = [NSString new]; <- release
everything else <- don't release.
there is exactly one valid use of dealloc. That is in [super dealloc]; in your - (void)dealloc method.
If the dictionary gets deallocated all the objects and keys it contains get released. If they are not retained somewhere else they get deallocated too.
There is plenty of documentation about memory management available. For example Apples Advanced Memory Management Programming Guide
what do you mean "remove"? assume you mean dealloc or free
str is retained by dict and if you didn't call retain on it than you should not call release on it.
and you should never call dealloc to any object
NSDictionary *dict = [NSDictionary dictionaryWithObjects....];
dict is an autoreleased object with means it will be deallocated on next run loop if you did not retain it anywhere
everything in dict is retained by it so they will not be deallocated until dict is deallocated
I have a memory leak problem that just can not understand! Watch this initialization method:
- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {
if (self = [super init]) {
NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
[[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera]];
self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
self.compositore = [[NSString alloc] initWithString:nomeCompositore];
self.tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
}
return self;}
Then this little variation (note self.tipologia):
- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {
if (self = [super init]) {
NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
[[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera]];
self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
self.compositore = [[NSString alloc] initWithString:nomeCompositore];
self.tipologia = [[NSString alloc] initWithString:#"Test"];
}
return self;}
In the first variant is generated a memory leak, the second is not! And I just can not understand why! The memory leak is evidenced by Instruments, highlighted the line:
[NSDictionary dictionaryWithContentsOfFile:pathOpere]
This is the dealloc method:
- (void)dealloc {
[tipologia release];
[compositore release];
[nomeCompleto release];
[super dealloc];}
Remember that alloc returns an object that you own.
If you declared your three string properties as retain, assigning those objects to your properties means you now own each one twice—once because you allocked it, and again because you assigned it to your property. The objects remain alive because nothing releases their second ownerships.
If you declared the properties as copy (which is the correct way to declare an NSString property), assigning the object there stores a copy as the value of the property. You do nothing further with the original objects, which remain alive because nothing releases them.
Either way, that is your leak.
The property should be declared as copy; if it already is, don't try to fix the leak by changing that.
You should not use property access here. Remember that assigning to a property is a set<PropertyName>: message, and that your object is not fully initialized yet. Sending a message to an incompletely-initialized or incompletely-deallocated object is asking for trouble, particularly when subclasses are involved, since they may override the accessor methods in ways the superclass doesn't expect.
So, in init only, assign directly to the instance variables. In dealloc only, send release messages directly to the objects in the instance variables. Everywhere else, use property accesses.
You also should not use alloc and initWithString: here. It'll work, but the convention is to send copy messages to the objects you already have, the same as the properties would do. Send copy messages to your input string objects, then assign the copies to your instance variables.
When you do use property accesses, use the convenience constructors (stringWith…:, for example), as these return objects that you do not own. When you assign these objects to your copy-declared properties, you will actually be storing copies that you do own.
The other way would be to use alloc and initWithWhatever:, then immediately autorelease that object before assigning it to the property; this way creates an object that you own, then immediately gives up ownership before assigning it to the property.
Try
nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
compositore = [[NSString alloc] initWithString:nomeCompositore];
tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
or
self.nomeCompleto = nomeOpera;
self.compositore = nomeCompositore;
self.tipologia = [dicOpera objectForKey:kKeyTipologia];
instead of self.xxx = [[yyy alloc] init...].
In the original code, the RHS of the assignment returns an object of retain count +1, and if you make the #property having (retain) or (copy), the final retain count would be +2. Therefore, even if you release these in -dealloc, the net retain count is +1, causing a memory leak.
BTW, there's no point calling +dictionaryWithDictionary:. Just use
NSDictionary* dicOpera = [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera];
Consider the following method, where I build a string and return it. I would like to release the building blocks of the string, but then the string is based on values that no longer exists. Now what?
Am I leaking memory and if so, how can I correct it?
- (NSString) getMiddahInEnglish:(int)day{
NSArray *middah = [[NSArray alloc] initWithObjects:#"Chesed", #"Gevurah", #"Tiferes", #"Netzach", #"Hod", #"Yesod", #"Malchus"];
NSString *firstPartOfMiddah = [NSString stringWithFormat: #"%#", [middah objectAtIndex: ((int)day% 7)-1]];
NSString *secondPartOfMiddah = [NSString stringWithFormat: #"%#", [middah objectAtIndex: ((int)day / 7)]];
NSString *middahStr = [NSString string#"%# She'bi#%", firstPartOfMiddah, secondPartOfMiddah];
[middah release];
[firstPartOfMiddah release];
[secondPartOfMiddah release];
return middahStr;
}
At the end of the method, the return string, middahStr has not been released. Is this a problem? Am I leaking memory?
Quite the opposite: You are over-releasing.
middah you alloc and init. Then you release it. All is well.
firstPartOfMiddah and secondPartOfMiddah, you call an NSString "stringWith" convenience method. This is equivalent to invoking alloc, init, and autorelease. Your releasing them is a mistake, as they are essentially given to autorelease to release later.
middahStr you call the convenience method, but return it. All is well. It will be released later by the autorelease pool.
Rule of thumb (I'm sure there are plenty of exceptions): If you don't alloc it, don't release it.
The function I'm looking at:
-(void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"statedictionary" ofType:#"plist"];
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.statesZips = dictionary;
[dictionary release];
NSArray *components = [self.stateZips allKeys];
NSArray *sorted = [components sortedArrayUsingSelector:#selector(compare:)];
self.States = sorted;
NSString *selectedState = [self.states objectAtIndex:0];
NSArray *array = [stateZips objectForKey: selectedState];
self.zips = array;
}
Why is an NSDictionary allocated, then assigned to a pointer called *dictionary, and then assigned to the instance variable stateZips? Why not allocate it and assign it directly to the instance variable and save memory of creating and releasing another NSDictionary? The same methodology is always followed, including later in this function with the NSArray...
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.statesZips = dictionary;
[dictionary release];
Also, this sorting puts the keys from a hash table (dictionary) in alphabetical order. I'm not sure I understand this line:
NSArray *sorted = [components sortedArrayUsingSelector:#selector(compare:)];
No one seems to have addressed the fact that the line
self.statesZips = dictionary;
is not directly an instance variable assignment. stateZips is a property, and so that line of code calls the setStateZips: method. That method retains or copies the dictionary, so unless the viewDidLoad method intends to use it again for some purpose, it's not needed any longer. That makes it OK to release it.
The previous line:
[[NSDictionary alloc] initWithContentsOfFile:plistPath];
allocates an object. That makes it your responsibility to release it once you don't need it any more. After assigning it to the statesZips property, it's no longer needed, so it's released and you shouldn't use dictionary any more. You'll notice that later code only refers to self.stateZips, not dictionary.
In the case of the NSArray later in the method, viewDidLoad does not allocate the object, so that method is not responsible for calling release on it. The rule of thumb is that if you alloc it, you're responsible for making sure it gets released. Otherwise, it's not your problem.
Sorting the array uses the sortedArrayUsingSelector: method. A selector identifies a method in Objective-C. And the #selector is the literal syntax for selectors (kind of like how #"" is the literal syntax for NSString objects). So, what that code says, is "give me an array where the objects in components are sorted, and use the compare: method to compare each object when you do the sort. When it sorts the array, it will call compare: on the objects in the array to determine how to put them in order.
The statesZips property is probably retained, that's the reasoning.
When the NSDictionary is first allocated, its retain count is 1. When it's assigned to statesZips, the retain count becomes 2. When it's released, the retain count drops to 1, which is usually the desired outcome.
Note that the code below would have produced (almost) the same result:
self.statesZips = [NSDictionary dictionaryWithContentsOfFile:plistPath];
because dictionaryWithContentsOfFile returns an autoreleased object.
As a convention, class methods like [NSDictionary dictionary] return autoreleased objects (which automatically get released after some time), while the usual alloc-init method (as in [[NSDictionary alloc] init]) return retained objects.
I suggest you read the Memory Management Programming Guide for Cocoa for further information.
EDIT: I must have missed the last part of your question when I first read it, but Barry has already answered that part.
This code uses reference-counted memory management (not the automatic garbage collection memory management available in Objective-C 2.0 on OS X). When any object (in this case, the NSDictionary and the NSArray) are alloc'd, the caller is responsible for calling -release on that instance. Failing to call release causes a memory leak. The code could have been written as
self.statesZips = [[[NSDictionary alloc] initWithContentsOfFile:plistPath] autorelease];
but at the expense of less explicit memory management (relying on NSAutoreleasePool to release the alloc'd instance at the end of the event loop iteration.
the call
[components sortedArrayUsingSelector:#selector(compare:)];
returns an array of whose elements come from components but according to the return value of calling [elem1 compare:elem2] to compare two array elements.