NSMutableArray Memory Leak Issue - iphone

There are multiple memory leaks in this section of my code. Specifically with these arrays: PlaylistItem, PlaylistItemID and PlaylistItemLength. The problem is that I can't successfully release the arrays. When I attempt to use insert [xxxx release]; anywhere in this code, the app locks up. It's driving me absolutely nurtz!
-(void)configureCueSet {
MPMediaQuery *myPlaylistsQuery = [MPMediaQuery playlistsQuery];
NSArray *playlists = [myPlaylistsQuery collections];
//Get # of items in a playlist and names -------------------------------------
NSArray *songs;
for (MPMediaPlaylist *playlist in playlists) {
NSString *playListItem = [playlist valueForProperty: MPMediaPlaylistPropertyName];
if ([playListItem isEqualToString: savedLastSelectedPlaylist]){
songs = [playlist items];
}
}
PlaylistItem = [[NSMutableArray alloc] init];
PlaylistItemID = [[NSMutableArray alloc] init];
PlaylistItemLength = [[NSMutableArray alloc] init];
for (MPMediaItem *song in songs) {
[PlaylistItem addObject:[song valueForProperty: MPMediaItemPropertyTitle]];
[PlaylistItemID addObject:[song valueForProperty: MPMediaItemPropertyPersistentID]];
[PlaylistItemLength addObject:[song valueForProperty: MPMediaItemPropertyPlaybackDuration]];
}
}

Does that method get called multiple times? If so, your leak likely occurs on that assignment. You'd want:
[PlayListItem release];
PlaylistItem = [[NSMutableArray alloc] init];
[PlayListItemID release];
PlaylistItemID = [[NSMutableArray alloc] init];
[PlaylistItemLength release];
PlaylistItemLength = [[NSMutableArray alloc] init];
If you don't release what was there before, then you'll get a leak.

Attempting to insert [xxx release] would release the contents, not the arrays. The application crashes because with that you are deallocating the object which you are about to add to the array. According to the documentation (here), the values in an NSArray are automatically retained, and will be released as soon as the array is dealloc'ed. So, if you want to release any of those arrays, simply type [PlaylistItem release].

Related

How to create separate instance for NSMutableArray

In my application I need to create a copy of NSMutableArray. Currently I am using mutableCopy method. But when I modify the copied array the original one is also modified. Please tell me How to create a new copy NSMutableArray.
Here is the code
delegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
self.mainImagesArray = [[NSMutableArray alloc]initWithArray:[delegate.arrayForCarCaptureImages mutableCopy]];
and here i am modifying
UIImage *filteredImage =[[[[self.mainImagesArray objectAtIndex:i]valueForKey:valueVheck] objectAtIndex:j] copy];
filteredImage =[filteredImage brightness:(1+sliderValue-0.5)];
[[[self.mainImagesArray objectAtIndex:i]valueForKey:valueVheck]removeObjectAtIndex:j];
[[[self.mainImagesArray objectAtIndex:i] valueForKey:valueVheck]insertObject:filteredImage atIndex:j];
After execution the arrayForCarCaptureImages also modified automatically.
You need to create a deep copy. From what I understand, you're creating a copy of the NSMutableArray itself without making copies of it's individual elements.
From what I see in the code you've written:
You're abusing the delegation pattern
Your code is not readable. Try passing it along to another developer, I bet you'll get slapped very fast :)
Here's an example of a classic case of deep copying:
NSArray *numbersArr = #[#1,#2,#3];
NSMutableArray *numbersArrCopy = [NSMutableArray array];
for (NSNumber *num in numbersArr) {
[numbersArrCopy addObject:[num copy]];
}
An easier approach:
NSArray *numbersArr = #[#1,#2,#3];
NSArray *numbersArrCopy = [[NSArray alloc] initWithArray:numbersArr copyItems:YES];
This is of course different than just:
NSArray *numbersArr = #[#1,#2,#3];
NSArray *numbersArrCopy = [numbersArr copy];
Have you tried:
NSArray *copiedArray = [[NSArray alloc] initWithArray:[otherArray copy]];
Try this code in your project:
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"one", #"two", #"three", nil];
NSMutableArray *copiedArray = [array mutableCopy];
[array addObject:#"four"];
NSLog(#"copied %#", copiedArray);
MutableCopy will not perform deep copy. You have to do it manually as given,
NSMutableArray *arrayCopy = [NSMutableArray array];
for (id element in arraySource)
[arrayCopy addObject:[element mutableCopy]]; //or [arrayCopy addObject:[element copy]];
You code is hardly readable. See if this is working
delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.mainImagesArray = [[NSMutableArray alloc]initWithArray:delegate.mainImagesArray
copyItems:YES];
UIImage *filteredImage = self.mainImagesArray[i][valueVheck][j];
filteredImage = [filteredImage brightness:(1+sliderValue-0.5)];
[self.mainImagesArray[i][valueVheck]replaceObjectAtIndex:j
withObject:filteredImage];

Declared and synthesized NSArray works in all methods but one

I have an array of NSMutableDictionaries which has been sorted.
This array has been declared and synthesized so that its reachable anywhere in the code. However, its not.
When I try to read it out in cellForRowAtIndexPath, I get 0x5852400 does not appear to point to a valid object in debugger by using the po command, and I get the EXC_BAD_ACCESS error when using NSLog.
Code:
- (void)request:(FBRequest *)request didLoad:(NSArray*)result
{
int counter;
friendsArray = [[NSMutableArray alloc] init];
for (NSDictionary *d in [result objectForKey:#"data"])
{
[friendsArray addObject:d];
counter++;
}
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"first_name" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
newfriendsArray = [[NSArray alloc] init];
newfriendsArray = [friendsArray sortedArrayUsingDescriptors:sortDescriptors];
NSLog(#"The new array which works and has been sorted: %#", newfriendsArray);
[[self tableView] reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSLog(#"Array still working here: %#", newfriendsArray);
return [newfriendsArray count];
}
Doing the same NSlog like the ones above in cellForRowAtIndexPath will cause the simulator to crash.
It's a memory management error. In this line:
newfriendsArray = [friendsArray sortedArrayUsingDescriptors:sortDescriptors];
sortedArrayUsingDescriptors: returns an autoreleased object that you need to retain if you want to use it for longer than the current method. The line above:
newfriendsArray = [[NSArray alloc] init];
has no effect other than introduce a memory leak to your code. It seems you have not quite understood when you need to create new objects and when other methods do this for you. In addition, you should really use properties to set your ivars (self.newFriendsArray = ...;) to avoid these simple memory management errors.

How to dissect and reorganize info in an NSDictionary

So I have an array of NSDictionaries, each NSDictionary has a bunch of key/value pairs pertaining to aspects of a photo (from Flickr).
I'm making an app that has a UITableViewController whose cells should be each of the different categories of the photos. So in pseudocode, I'm trying to construct a new NSDictionary (with keys being categories of photos, values being the NSDictionaries of the photos that contains that key). I'm iterating through each NSDictionary in the initial array, getting the category tags, and saying, if my new NSDict doesn't contain this key, make a new key to an empty array. Then add the current NSDict to that array. I'm getting consistent errors, not sure why.
Here's the diluted code.
photoList = [FlickrFetcher photosWithTags:[NSArray arrayWithObjects: #"CS193p_SPoT", nil]];
NSLog(#"%#", photoList);
categories = [[NSDictionary alloc] init];
NSArray *temp = [[NSArray alloc] init];
for (id obj in photoList) {
temp = [[obj objectForKey:#"tags"] componentsSeparatedByString:#" "];
for (id string in temp) {
if (![categories objectForKey:string]) {
NSMutableArray *arr = [[NSMutableArray alloc] init];
[categories setObject:arr forKey:string];
//[arr release];
}
NSMutableArray *photos = [categories objectForKey:string];
[photos addObject:obj];
[categories setObject:photos forKey:string];
}
}
Thanks!
NSDictionary doesn't have a method setObject:forKey:. You need an NSMutableDictionary.
self.categories = [NSMutableDictionary dictionary];
Other than that, please do use Joost's excellent rewrite of your code.
SIGABRT, just so you know, most likely means that an assertion somewhere failed. In this case, it may be an assertion all the way down in CoreFoundation*; CF checks for mutability when you try to access a dictionary like that and causes an interrupt if the object isn't mutable.
*I have just learned about the CF source's availability recently and have been looking through it, so this may be just "new thing" bias and incorrect.
I don't notice any errors (syntax-errors, that is) in your code, however here is an updated piece of code which has been implemented a bit cleaner (and without memory leaks)
self.photoList = [FlickrFetcher photosWithTags:[NSArray arrayWithObjects: #"CS193p_SPoT", nil]];
NSLog(#"%#", photoList);
self.categories = [NSDictionary dictionary];
for (NSDictionary *obj in photoList) {
NSArray *temp = [[obj objectForKey:#"tags"] componentsSeparatedByString:#" "];
for (NSString *string in temp) {
NSMutableArray *photos = [categories objectForKey:string];
if (!photos) {
photos = [NSMutableArray array];
[categories setObject:photos forKey:string];
}
[photos addObject:obj];
}
}
If it's not working please tell us the exact warning, and were it is caused.

NSString leaking even with release at the right place (I guess)?

When I analyze the following code with Instruments, it reports a leak on variable imageName:
//loadImagesFromPotatoesIndexesArray
-(void) loadImagesFromPotatoesIndexesArray{
//Load Textures from Disk
textures = [[NSMutableArray alloc] init];
//NSArray *masks = [[NSArray alloc] initWithArray:mainDelegate.masksArray];
for (int i = 0;i<[potatoesIndexesArray count];i++){
int imageNumber = [[potatoesIndexesArray objectAtIndex:i]intValue];
NSString *imageName = [[NSString alloc] initWithFormat:#"texture%d",imageNumber];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:#"png"]];
NSArray *pics = [[NSArray alloc] initWithObjects:
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
imageName,
nil];
[textures addObject:pics];
[image release];
[imageName release];
[pics release];
}
}
[potatoesIndexesArray count] = 16, so I've got 16 times that NSCFString leaking ... But to me the code is respecting memory management ... obviously not!!!
What did I do wrong?
You never release the 'textures' array. It's still holding everything.
How often is loadImagesFromPotatoesIndexesArray called in your code? If is called more than once, all of the values in the original array will be leaked, since you don't properly release textures before replacing it with a new array.
If it is being called more than once, this should do the trick:
// load textures from disk
[textures removeAllObjects];
//NSArray *masks = [[NSArray ...
for (int i=0; ...
If think that when you add imageName in your pics array it retain it ;-)
(I think it answer to your question)
But, why are you doing a alloc here ?
why not doing something like
[ NSString stringWithFormat:#"" ] ?
Good Luck !
This is a complicated issue. You alloc the imageName, so the retainCount is 1, then you add it into an array, the retain count is 2, when you release the imageName, the retain Count is 1 again. Then if you also release the pics array, everything will be fine. But your pics array is added into textures, then the pics is released, so your pics retainCount is still 1. And your imageName is leaked. But, if you release the textures array, everything will be fine
NSString *imageName = [[NSString alloc] initWithFormat:#"texture%d",imageNumber];
NSArray *pics = [[NSArray alloc] initWithObjects:
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
imageName,
nil];
[imageName release];

memory management question -- releasing an object which has to be returned

I have an NSMutableArray called playlist. This is in a method called getAllPlaylists. The code is something like this:
-(NSMutableArray *)getAllPlaylists
{
//playlist is an instance variable
playlist = [[NSMutableArray alloc] init]; //memory leak here
...
//some code here which populates the playlist array
[playlist addObject: object1];
...
return playlist;
}
The array allocation step of playlist is causing a memory leak. In such a scenario where can i release this array? Or can i avoid allocation n initialization of playlist here by doing something else? Any help will be greatly appreciated!!
2 solutions:
Use autorelease:
- (NSMutableArray*)getAllPlaylists
{
playlist = [[NSMutableArray alloc] init];
...
return [playlist autorelease];
}
or instead of using [[NSMutableArray alloc] init] to create your NSMutableArray object, use [NSMutableArray array] which is equivalent to [[[NSMutableArray alloc] init] autorelease]:
- (NSMutableArray*)getAllPlaylists
{
playlist = [NSMutableArray array];
...
return playlist;
}
You should autorelease newly created objects that you want to return that are not owned by the object (local variables, not instance variables).
playlist = [[[NSMutableArray alloc] init] autorelease];
Alternatively, you can use the convenience method to do that more easily:
playlist = [NSMutableArray array];
For items the object owns (instance variables), you should make sure you release the old value first and implement a dealloc method that also releases the value.
- (NSMutableArray*)getAllPlaylists {
[playlist release];
playlist = [[NSMutableArray alloc] init];
return playlist;
}
- (void)dealloc {
[playlist release];
[super dealloc];
}
For more info, see the memory management guide.