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];
Related
I am collecting response into the variable
-(NSMutableDictionary *)getCombineIdAndNames{
NSMutableDictionary *lObjCombineIdAndNamesArrayPtr = [[NSMutableDictionary alloc] init];
[lObjCombineIdAndNamesArrayPtr setObject:lObjtempNamePtr
forKey:lObjtempIdPtr];
return [lObjCombineIdAndNamesArrayPtr autorelease];
}
This is causing memory leak
gObjAppDelegatePtr.m_cObjCombineIdNameDictPtr = [gObjAppDelegatePtr.m_cDbHandler getCombineIdAndNames];
gObjAppDelegatePtr.m_cObjCombineIdNameDictPtr. That is a property of type copy. But it still giving memory leak. How to fix it.Please help me.
The getCombineIdAndNames is perfectly fine. You are allocing the dictionary and autoreleasing it before you return. Nothing wrong there.
This would suggest to me that the memory leak is being caused by the gObjAppDelegatePtr instance. Either it isn't releasing its property or maybe the whole object is being leaked.
As an aside, one thing you could improve in getCombineIdAndNames is to use the convenience constructor of NSMutableDictionary to avoid all alloc/release calls completely. You can also use the new Obj-C container syntax:
-(NSMutableDictionary *)getCombineIdAndNames{
NSMutableDictionary *lObjCombineIdAndNamesArrayPtr = [NSMutableDictionary dictionary];
lObjCombineIdAndNamesArrayPtr[lObjtempIdPtr] = lObjtempNamePtr;
return lObjCombineIdAndNamesArrayPtr;
}
I'm trying to save data to and XML file on Iphone. For that, I load the wholeXML, add new data and the save it again. The problem arises when i try to store the new data, my
[mArray addObject:newData];
methods crashes, as mArray is not a NSMutableArray, instead, it is a NSCFArray even if I applied a mutableCopy method to it.
As I understand, a NSCFArray is a toll-free bridging to an NSArray, so I can't understand why the mutablyCopy method is not working.
Any idea??
NSMutableDictionary *wholeXML = [[NSMutableDictionary alloc] init];
wholeXML = xmlData;
NSArray *array = [[NSArray alloc] init];
NSMutableArray *mArray = [[NSMutableArray alloc] init];
array = [wholeXML objectForKey:#"Key"];
mArray = [a mutableCopy];
NSCFArray is a private subclass that gets instantiated when you do things with NSArray factory methods or initializers. You're doing too many initializations. Try this simplified version:
NSMutableDictionary *wholeXML = [[NSMutableDictionary alloc] initWithDictionary:xmlData];
NSMutableArray *mArray = [[NSMutableArray alloc] initWithArray:[wholeXML valueForKey:#"Key"]];
NSCFArray is the concrete class for both NSMutableArray and NSArray. It sounds like you are simply mistaken about what kind of array you have. Since the code you posted is obviously not your real code (it won't even compile, and wouldn't exhibit the problem even if it did), it's impossible to tell at what point your program is assigning an immutable array to the variable. But that's what it sounds like is happening.
I will say (and please don't take this as a personal criticism — it's just an observation) that the code you posted suggests you don't have a strong grasp on how classes and object identity work. That's probably the root cause here.
All three of your variables you initialize with [[Something alloc] init], but then you immediately throw away the object and replace it with something else. This means the original object (NSMutableArray in this case) just gets leaked and the variable now contains the new object you have assigned. If that new object isn't an NSMutableArray, it won't magically be turned into one just because that's what the variable held before.
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.
When adding items to NSMutableDictionary using the setValue:forKey: method (I suppose this generalizes to any NSObject) does the dictionary retain the second parameter, the NSString?
For example:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSString *theString = #"hello";
int i;
for (i=0; i<[theString length]; i++){
NSNumber *myInt = [NSNumber numberWithInt:i];
NSString *character = [NSString stringWithFormat:#"%C",[theString characterAtIndex:i]];
[dict setValue: myInt forKey:character];
}
[dict release];
[pool release];
Clearly, there is no reason to release myInt in the loop, it is retained by dict so it can't be released until the end of the code. But is the same true of character? My thinking is that if NSMutableDictionary stores the string in some other way, then one could create a temporary pool around the loop and release those strings instead of waiting until the release of the dictionary.
I am also curious as to why retainCount of character is 7fffffff as if it is an NSConstantString, I would expect stringWithFormat to return an NSString object which would need retaining, but that doesn't seem to be the case.
It's very common in Cocoa for NSString parameters to be copied instead of retained. That's because you could have just as easily given the dictionary an instance of NSMutableString. Because the string's value could change, NSDictionary makes a copy.
But, regardless of how NSMutableDictionary really operates, you don't have to worry whether character needs to be retained. Once you've passed it to NSMutableDictionary as a parameter, it's really that class's problem to decide how to store the data, unless the documentation specifically tells you that retaining the objects are your responsibility.
I also wouldn't worry too much about the retainCount of any object. Following the retain count of an object too closely can lead you down rabbit holes that just make you spin your wheels.
Finally, I really don't think you need to create your own autorelease pool here. Unless you know with absolute certainty that theString is going to be very long, or you've already observed high memory utilization in Instruments, adding the autorelease pool is an unnecessary optimization.
You don't need to retain character there, the dictionary retains it when you set it as a key and your own code has no need to retain it.
You also don't need to worry about why the retain count isn't what you expect. Maybe the Foundation framework has Flyweight-like instances of a load of single-character NSString instances. In any case if you've got the memory management correct following the guidelines, you'll be OK regardless of what the framework's doing behind the scenes. http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html
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.