Should I release array returned from [NSMutableDictionary ValueForKey: ] - iphone

I have a NSMutableDictionary with the key being the first alphabet of the name of an object. The view is something like the 'Contacts' tab on iphone. Additionally user can select individual objects in the list.
In the code I find each selected object to process them further.
NSMutableArray *objectsToAdd = [[NSMutableArray alloc] init];
NSMutableArray *array = nil;
for (NSString *key in self.nameIndex) {
array = (NSMutableArray *)[searchedNameDictionary valueForKey:key];
for (Objects *eachObject in array) {
if (eachObject.objectIsSelected){
[objectsToAdd addObject:eachObject];
}
}
}
[array release];
-(void)dealloc()
{
[searchedNameDictionary release];
}
The app is crashing where I release searchedNameDictionary, with the message that the deallocated object is being referenced.
Now if in the code above, I remove [array release] the app works fine.
My question is does releasing 'array' is actually releasing the objects in searchedNameDictionary, which is what seems to be happening.
Would not releasing array cause memory leak?

You shouldn't release returned object unless they come from an alloc or copy method.
Returned objects are autoreleased otherwise, if you want to keep it around your should retain it right after receiving it.

array = (NSMutableArray *)[searchedNameDictionary valueForKey:key];
This returns an autoreleased object, thus you don't need to release it.
There are some other...issues with your code too, but mostly style things. Get rid of the [array release] and you're good to go as far as that issue is concerned.

Related

iPhone - Problems with releasing memory - memory leaks

I'm having the code as below.
- (void)viewDidLoad
{
NSArray* myarr = [self createArray];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
[myarr release];
}
-(NSArray*)createArray
{
NSArray* arr1 = [[NSArray alloc] initWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
return arr1;
}
When I "Build & Analyze", its showing two leaks. One at [myarr release] saying, incorrect decrement of the reference count of an object that is owned at this point. and Other at return arr1, saying, Potential leak of an object allocated on line 152 and stored into arr1.
From my above code, the method "createArray" is returning a pointer and I'm releasing it as well. Is my way of coding right or wrong?
From my above code, the method "createArray" is returning a pointer and I'm releasing it as well. Is my way of coding right or wrong?
that depends on how you look at it.
1) the ref counting looks ok
2) the static analyzer flags objc methods based on names, in some cases. so the issue will likely vanish if you rename createArray to newArray, or something named new*. so it expects a convention (the ones used by Apple) to be followed.
therefore, it's the message that's bit shallow, it doesn't really analyze the program, but bases its findings/results on convention -- and not an actual evident issue which a human can read.
If you're just using the array in your viewDidLoad method, then you don't need to alloc an array in there at all. You can just use an autoreleased array returned as 7KV7 suggested. You can return an autoreleased array in your -(void)createArray as well without alloc'ing an object. Here is an example.
- (void)viewDidLoad
{
NSArray* myarr = [self createArray];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
}
-(NSArray*)createArray
{
return [NSArray arrayWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
}
If you don't have to alloc an object to use it, it makes for less, and cleaner code, IMO.
Try this
- (void)viewDidLoad
{
NSArray* myarr = [[NSArray alloc] initWithArray:[self createArray]];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
[myarr release];
}
-(NSArray*)createArray
{
NSArray* arr1 = [[NSArray alloc] initWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
return [arr1 auotrelease];
}
The problem with your code is that
You do not allocate myarr using alloc or new so you do not take ownership of the object. Hence the issue in release.
You allocate arr1 so you take ownership of the object and you return arr1. Hence you do not release it. That is the reason for the leak.

Can't Figure Out How To Fix Memory Leaks on iPhone

I was running Leaks tool and discovered a massive leak in my Dictionary mutableDeepCopy but I can't figure out what's wrong with the code. Any suggestions?
#interface RootViewController : UIViewController{
NSDictionary *immutableDictionary;
NSMutableDictionary *mutableDictionary;
}
Here is the line of code that's highlighted in Instruments
self.mutableDictionary = [self.immutableDictionary mutableDeepCopy];
Here is the method for creating a mutable copy of a Dictionary
#interface NSDictionary(MutableDeepCopy)
-(NSMutableDictionary *)mutableDeepCopy;
#end
Here is method implementation, I've highlighted the code that Leaks saids is leaking 100%
- (NSMutableDictionary *) mutableDeepCopy {
NSMutableDictionary *dictionaryToReturn = [NSMutableDictionary dictionaryWithCapacity:[self count]];
NSArray *keys = [self allKeys];
for(id key in keys) {
id value = [self valueForKey:key];
id copy = nil;
if ([value respondsToSelector:#selector(mutableDeepCopy)]) {
copy = [value mutableDeepCopy];
} else if ([value respondsToSelector:#selector(mutableCopy)]) {
copy = [value mutableCopy]; //This is the Leak
}
if (copy == nil) {
copy = [value copy];
}
[dictionaryToReturn setValue:copy forKey:key];
}
return dictionaryToReturn;
}
You need to analyse this in light of Apple's Memory Management Rules.
Starting with this line:
self.mutableDictionary = [self.immutableDictionary mutableDeepCopy];
I would expect mutableDeepCopy to return an object I own, so at some point I need to release or autorelease it. e.g.
NSMutableDeepCopy* temp = [self.immutableDictionary mutableDeepCopy];
self.mutableDictionary = temp;
[temp release];
or
self.mutableDictionary = [[self.immutableDictionary mutableDeepCopy] autorelease];
So now we need to look at mutableDeepCopy. Because it has 'copy' in the name it needs to returned an "owned" object which, in practice means "forgetting" to release the returned object. You have already failed to do that when you create the returned object in the first line, since dictionaryWithCapacity: gives you an object you do not own. Replace it with
NSMutableDictionary *dictionaryToReturn = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
Now you own it.
It is important that you make your mutableDeepCopy obey the rules because it means you can treat the objects returned from mutableDeepCopy, mutableCopy and copy in exactly the same way. In all three cases you own the object copy that you insert into the array. Because you own it, you must release it or it'll leak as you found out. So, at the end of the loop, you need
[copy release];
That'll stop the leak.
How is your property declared? If is is retain or copy, then this doesn't leak.
Your problem is that the name mutableDeepCopy suggests that it returns a retained object, and not an autoreleased one as it actually does.
Edit:
And at the mutableDeepCopy itself, you need to release the copy variable after adding to the dictionary.
mutableCopy increments the retain count of the object, as does setValue:forKey:. This means that when dictionaryToReturn is dealloc'ed, the object that had mutableCopy called still has a retain count of one.
Try doing this instead:
copy = [[value mutableCopy] autorelease];

NSMutableArray not retaining the value even after setting the property

In my iphone app i have implemented the SearchBar on TableView.
Now the SearchBar Searches the items but the searched items are not shown in the tableview as the NSMutableArray which fills up the table with the search results is not retaining the values.
I have put the screenshot of the code and the NSLog statements for the count of copyListOfItems always return 0 even though the NSLog(#"%#",sTemp); shows the searched items in Console.
I have created the property for the NSMutableArray copyListOfItems and also synthesized it but its count in the Console is always shown as Zero.
Here is the Code:
searchArray = [Food mutableCopy];
for (NSString *sTemp in searchArray)
{
NSLog(#"Value: %#",NSStringFromClass([sTemp class]));
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
{
NSLog(#"sTemp is:%#",sTemp);
NSLog(#" is:%#",sTemp);
[copyListOfItems addObject:sTemp];
[copyListOfItems retain];
}
NSLog(#"Copy list count in Search Delegate Method inside For : %d",[copyListOfItems count]);
}
NSLog(#"Copy list count in Search Delegate Method : %d",[copyListOfItems count]);
[searchArray release];
searchArray = nil;
searching = YES;
[self.table reloadData];
de:
What should I do?
Please Help
Please Suggest
Thanks
From what you've said, it sounds as though the copyListOfItems array is never created. Synthesizing the property creates a pair of accessor methods, but doesn't automatically create an instance of NSMutableArray for you.
So somewhere, you'll need to call [[NSMutableArray alloc] init] (or some variant of init...), and assign the result to the instance variable, or call [NSMutableArray array] (or a variant of array...), and pass the result as an argument to the setCopyListOfItems: method.
I can only assume that you're over-releasing the items in the array. It appears that you do not have a good grip on the retain/release concept, since you're over-retaining the NSArray copyListOfItems. You shouldn't be calling -retain on the same object within a loop since you're incrementing a single object's retain count by 1 for each iteration of the loop.
That aside, what does this code output in its NSLog() calls? It doesn't make a lot of sense. Why do you try to take a mutable copy of (what appears to be) a class called Food?
What exactly is "Food"? An objective-C object, or an objective-C class? This should not be compiling at all in my opinion, unless it's just very badly named.
Make sure you always alloc init the NSMutableArray. If you don't do that, all the items you add will just euhm.. disappear. So make sure you do this somewhere:
searchArray = [[NSMutableArray alloc] init];
Oops..!!!! Cleaned my project and build and run again and it has started working.Thanks guys for being so helpful.. :]
where are you creating the copyListOfItems Array?
I guess copyListOfItems is nil. And you are trying to add something to nil array and trying to retain the nil.
Note: whatever the operation you do on the nil will not be affected. Check whether you are creating the copyListOfItems array properly.
And why are you trying to retain the same array after adding each object?

[CFArray release]: message sent to deallocated instance

I'm using the following method in my code:
- (NSMutableArray *) newOrderedArray:(NSMutableArray *)array ByKey:(NSString *)key ascending:(BOOL)ascending {
NSSortDescriptor *idDescriptor = [[NSSortDescriptor alloc] initWithKey:key ascending:ascending];
NSArray *sortDescriptors = [NSArray arrayWithObject:idDescriptor];
NSArray *orderArray = [array sortedArrayUsingDescriptors:sortDescriptors];
[idDescriptor release];
NSMutableArray *result = [NSMutableArray arrayWithArray:orderArray];
return result;
}
Is this a well-coded convenience method? As I think, it returns an autoreleased NSMutableArray.
This method is called by another one:
- (id) otherMethod {
NSMutableArray *otherResult = [[[NSMutableArray alloc] initWithCapacity:[otherArray count]] autorelease];
// I add some stuff to otherResult and then...
NSMutableArray *result = [dbUtils newOrderedArray:otherResult ByKey:#"objectId" ascending:NO];
return result;
}
This method (otherMethod) is called in some view controller where I want to store returned array and release it when deallocating the view controller. However, when [result retain] is called in this view controller (because I need it to be available and I can't allow it to be deallocated) I receive the following error:
[CFArray release]: message sent to deallocated instance
I've tried to log [result retainCount] just before calling retain and it print "1". I don't understand why an error is thrown when calling retain.
Thank you,
A
I don't see anything technically wrong with the code above--otherMethod should return an autoreleased NSMutableArray. Are you sure you're getting the error when calling retain? It looks more like you might be accidentally be sending release at some point instead of retain.
Stylistically, there's one thing--methods with "new" in the title should always return non-autoreleased objects, so you should either name your method something else (such as orderedArray...) or use [[NSMutableArray alloc] initWithArray:] instead of arrayWithArray. Also, method signatures shouldn't start with a capital (so ByKey should be byKey.
try this:
NSMutableArray *otherResult = [[NSMutableArray initWithCapacity:[otherArray count]];
Because initWithCapacity will return an autoreleased Array. Right now you tell the Autoreleasepool to release the Array twice.
initWithCapacity:does not return an autoreleased object. – Wevah
AFAIK initWithCapacity is a convenience initializier, which by convention return autoreleased objects. So if the object is only used within a local method, the autoreleasepool should deallocate it. Care to elaborate?

Why am I having trouble with a deep copy in Objective C?

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.)