I found an answer to this a while ago, and made a mental note to fix it, but now I can't for the life of me find the post again.
Very simple - my current method for adding dictionaries to an array is leaky. Please, what is the best way to ensure they are being released properly? My method:
[beachPresenters addObject:[[NSMutableDictionary alloc]initWithObjectsAndKeys:
#"Kayak rides",#"name",#"kayak_sm.png",#"smPhoto",#"kayak_med",#"medPhoto",#"Free kayak rides for kids",#"description",#"",#"Friday",
#"All day! 10.00am - 6.00pm",#"Saturday",#"",#"Sun",#"Beach",#"stage",#"Blah blah blah",#"blurb",nil]];
beachPresenters (I assume it's an array) takes ownership of the dictionary, so the +1 to the reference count caused by +alloc/-init of the dictionary is not balanced. Thus, the dictionary is leaked.
Use the convenience method equivalent to balance the retain:
NSDictionary *presenter = [NSDictionary dictionaryWithObjectsAndKeys: ...];
[beachPresenters addObject: presenter];
Your array beachPresenters retains the mutable array you created, but your array as created in your example already has a retain count of one. So even if you dispose of beachPresenters, your dictionary will still be retained, i.e., leaked.
Use [NSMutableDictionary dictionaryWithObjectsAndKeys:] instead.
You can have implementation like below:
NSMutableDictionary *dict = [[NSMutableDictionary alloc]initWithObjectsAndKeys:
#"Kayak rides",#"name",#"kayak_sm.png",#"smPhoto",#"kayak_med",#"medPhoto",#"Free kayak rides for kids",#"description",#"",#"Friday",
#"All day! 10.00am - 6.00pm",#"Saturday",#"",#"Sun",#"Beach",#"stage",#"Blah blah blah",#"blurb",nil];
[beachPresenters addObject:dict];
[dict release];
or
NSMutableDictionary *dict = [[[NSMutableDictionary alloc]initWithObjectsAndKeys:
#"Kayak rides",#"name",#"kayak_sm.png",#"smPhoto",#"kayak_med",#"medPhoto",#"Free kayak rides for kids",#"description",#"",#"Friday",
#"All day! 10.00am - 6.00pm",#"Saturday",#"",#"Sun",#"Beach",#"stage",#"Blah blah blah",#"blurb",nil] autorelease];
Related
I implement a method to return a dictionary in my app. But I find a memory leak using instrument, I tried to figure it out, but I still cannot find it. Can anyone help me out?
Thanks in advance, here is the code for that methods:
-(NSMutableDictionary *)initDict
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
[dict setObject:self.name forKey:#"Name"];
//Some similar set object for key here...
return dict;
}
I think the problem is from allocing memory for dict and not releasing it. But in the method, it seems I cannot release dict. So is there any method to fix the leak?
All variants are good. Here is third variant (choose wisely):
Replace
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
with
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
I think you just need to change the last line of initDict to this:
return [dict autorelease];
When you are creating any object in your function and you want to return them then you should always create it in a way that those objects are autoreleased. So you should change your code like below.
NSMutableDictionary *dict = [[[NSMutableDictionary alloc]init]autorelease];
So whenever you create any object just call autorelease method on that.
I have a method like this
- (NSDictionary *)getCellValuesForRow:(int)row {
NSMutableDictionary *dictValues= [[NSMutableDictionary alloc] init];
Outage *outage = [listOutage objectAtIndex:row];
[dictValues setObject:outage.duration forKey:#"OutageDuration"];
return dictValues;
}
and this value is stored in this way
NSDictionary *dict = [[NSDictionary alloc] initWithDictionary:[self getCellValuesForRow:(indexPath.row-1)]];
how to release memory in this scenario
This is what autorelease is for.
NSMutableDictionary *dictValues= [[[NSMutableDictionary alloc] init] autorelease];
You should autorelease dictValues in getCellValuesForRow, or just don't alloc it. This will keep it autoreleased:
NSMutableDictionary *dictValues= [NSMutableDictionary dictionary];
In most cases it should be the responsibility of whatever calls it to alloc it (if it needs to be kept around after the autorelease pool is cleared), then dealloc it later.
If whatever calls it doesn't need it kept around, it can just leave it autoreleased.
An alternative is to simply use
NSMutableDictionary *dictValues= [NSMutableDictionary dictionary];
That's effectively the same thing as what Dan suggested. Just less typing.
It applies to your next line, too:
NSDictionary *dict = [NSDictionary dictionaryWithDictionary:[self getCellValuesForRow:(indexPath.row-1)];
I have a simple code:
NSMutableArray *arrayCheckList = [[NSMutableArray alloc] init];
[arrayCheckList addObject:[NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"2011-03-14 10:25:59 +0000",#"Exercise at least 30mins/day",#"1",nil] forKeys:[NSArray arrayWithObjects:#"date",#"checkListData",#"status",nil]] ];
[arrayCheckList addObject:[NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"2011-03-14 10:25:59 +0000",#"Take regular insulin shots",#"1",nil] forKeys:[NSArray arrayWithObjects:#"date",#"checkListData",#"status",nil]]];
Now I want to add a specific index of above array to a dictionary. Below are two way, which one is better and why? What are the specific drawbacks of the latter?
NSDictionary *tempDict = [[NSDictionary alloc] initWithDictionary:[arrayCheckList objectAtIndex:1]];
OR
NSDictionary *tempDict = [arrayCheckList objectAtIndex:1];
What would the impact on the latter since I am not doing any alloc/init in it?
1:
NSDictionary *tempDict = [[NSDictionary alloc] initWithDictionary:[arrayCheckList objectAtIndex:1]];
Creates a new immutable dictionary object as a copy of the original one. If you add objects to the mutable dictionary in your arrayCheckList they will not be added to your copied reference.
2:
NSDictionary *tempDict = [arrayCheckList objectAtIndex:1];
This directly pulls the mutable dictionary from your array and not a copy. The following two lines will be equivalent:
[[arrayCheckList objectAtIndex:1] addObject:something];
[tempDict addObject:something];
The first one potentially copies the dictionary a index 1 of the array. (It should, since you're creating an immutable dictionary but the one in the array is mutable.) The second only gets a reference to the dictionary in the array -- there's no chance of creating a new object.
Is it acceptable to have a NSMutableArray within an NSDictionary? Or does the NSDictionary also have to be mutable?
The NSMutableArray will have values added to it at runtime, the NSDictionary will always have the same 2 NSMutableArrays.
Thanks,
Dan
Yes, it's perfectly acceptable. Keep in mind, the contents of the array are the pointers to your NSMutableArrays--those are what can't change in the immutable dictionary structure. What the pointers point to can change all you want. To wit:
NSMutableArray *arr = [[NSMutableArray alloc] init];
NSDictionary *dict = [NSDictionary dictionaryWithObject:arr forKey:#"test"];
[arr addObject:#"Hello"];
NSString *str = [[dict objectForKey:#"test"] objectAtIndex:0];
NSLog("%#", str);
It's quite acceptable. But, it's precisely the sort of setup that suggests you should seriously consider replacing the dictionary with an NSObject subclass that sports two properties for accessing the arrays.
I'm assuming my understanding of how to perform a deep copy isn't just there yet. The same with some sub-optimal memory handling that I'm performing down below. This code below probably depicts a shallow copy, and I believe that's where my problem might be. I have some cookie-cutter code for an example that looks like the following:
NSArray *user = [[xmlParser createArrayWithDictionaries:dataAsXML
withXPath:kUserXPath] retain];
if([user count] > 0) {
self.name = [[user valueForKey:#"name"] copy];
}
// Crash happens if I leave the next line un-commented.
// But then we have a memory leak.
[user release];
[xmlParser release];
Unfortunately when I comment out [user release], the code works, but we have an obvious memory leak. The method createArrayWithDictionaries:withXPath: was refactored last night when the SO community helped me understand better memory management. Here's what it looks like:
- (NSArray *)createArrayWithDictionaries:(NSString *)xmlDocument
withXPath:(NSString *)XPathStr {
NSError *theError = nil;
NSMutableArray *dictionaries = [NSMutableArray array];
CXMLDocument *theXMLDocument = [CXMLDocument alloc];
theXMLDocument = [theXMLDocument initWithXMLString:xmlDocument
options:0
error:&theError];
NSArray *nodes = [theXMLDocument nodesForXPath:XPathStr error:&theError];
for (CXMLElement *xmlElement in nodes) {
NSArray *attributes = [xmlElement attributes];
NSMutableDictionary *attributeDictionary;
attributeDictionary = [NSMutableDictionary dictionary];
for (CXMLNode *attribute in attributes) {
[attributeDictionary setObject:[attribute stringValue]
forKey:[attribute name]];
}
[dictionaries addObject:attributeDictionary];
}
[theXMLDocument release];
return dictionaries;
}
I'm guessing there's a couple of issues that might be going on here:
Auto release on my dictionaries array is happening, thus my app crashing.
I'm not performing a deep copy, only a shallow copy. Thus when the user array is released, self.name is done for.
With NSZombieEnabled, I see the following:
*** -[CFString respondsToSelector:]:
message sent to deallocated instance 0x1ae9a0
Also, the final call where the backtrace shows this is crashing contains the following code in a separate module from the other two methods:
User *u = self.user;
NSString *uri = [NSString stringWithFormat:#"%#/user/%#/%#",
[self groupName], u.userId, kLocationsUri];
Between all the auto releasing/copies/retain happening between the client code and createArrayWithDictionaries:withXPath, I'm a bit confused as to the real problem here. Thanks again for helping me understand.
OK, you don't need to retain the return value from createArrayWithDictionaries: since you're not keeping it around. The return value is autoreleased. I'd strongly recommend reading up on how autoreleasing works. You only retain things that you intend to keep around in your object.
Also, user is an NSArray. If you call [user valueForKey:#"name"], you'll get another NSArray of values representing the values of the name key for each of the objects in users. Furthermore, how is the name property on your object defined? If you declared it as copy or retain (I believe retain is the default if you don't specify it yourself), you don't need to copy or retain the value. Indeed, the accessor should always be responsible for doing the memory management, not the caller. If you wrote your own accessor (i.e. you didn't use the #synthesize keyword), you need to make sure you do the memory management there.
I'm guessing what you meant to write was something more like this:
NSArray *user = [xmlParser createArrayWithDictionaries:dataAsXML withXPath:kUserXPath];
if ([user count] > 0)
self.name = [[user objectAtIndex:0] objectForKey:#"name"];
[xmlParser release];
I think your troubles are stemming from a misunderstanding of how memory management works in Objective-C.
Hope this helps.
Auto release on my dictionaries array is happening, thus my app crashing.
If the caller intends to keep the array around somewhere, it needs to retain it. Otherwise, it will crash when it tries to access the (now-deceased) object.
If the caller is going to store it in a property, it must use the self.dictionaries = […] syntax, not dictionaries = […]. The former is a property access, which calls the setter method; the latter is a direct instance variable assignment.
Coming back to your actual question, that of a deep copy: You need to get the sub-elements of every element and put them in each element's dictionary.
Basically, you need a recursive method (or a queue, but that's harder—file under premature optimization until you've proven you need it) that takes an element and returns a dictionary, and then you need to call this method on each of your element's child elements, and collect the results into an array and put that into the dictionary you're creating.
I would recommend making this recursive method an instance method of the element. Something like:
- (NSDictionary *) dictionaryRepresentation {
NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary];
for (CXMLNode *attribute in attributes) {
[attributeDictionary setObject:[attribute stringValue] forKey:[attribute name]];
}
NSArray *childElements = [self childElements];
return [NSDictionary dictionaryWithObjectsAndKeys:
attributeDictionary, #"attributes",
[childElements valueForKey:#"dictionaryRepresentation"], #"childElements",
nil];
}
Then you replace the loop in createArrayWithDictionaries:withXPath: with a similar valueForKey: message. I'll leave you to fill it in.
valueForKey: is Key-Value Coding's principal method. In both places, we're making use of NSArray's handy implementation of it.
(If the use of valueForKey: still doesn't make sense to you, you should read the KVC Programming Guide. KVC is vitally important in modern Cocoa, so you do need to read this sooner or later.)