I am using a singleton class to share data between views in my iphone app. My singleton class contains a dictionary which I allocate in my -init method:
- (id)init
{
if ( self = [super init] )
{
self.dataList = [[NSMutableDictionary alloc]init];
}
return self;
}
I release it in my dealloc method:
- (void)dealloc
{
[dataList release];
[super dealloc];
}
This dataList is downloaded from a server, and I do this multiple times in my app,so I have a custom setter method to release the old one, and retain the new one:
-(void) setDataList:(NSMutableDictionary*)d
{
if( dataList !=nil){
[dataList release];
dataList = [d retain];
else
dataList = [d retain];
}
ON using the leaks tool, I am getting a memory leak of the dictionary. I think I am doing the alloc and release of the dictionary properly..does the leak occur because the dealloc method of the singleton is not getting called?
Thanks for your help,
Srikanth
Add an autorelease:
self.dataList = [[[NSMutableDictionary alloc] init] autorelease];
When you assign a an object to a property it retains it and whenever you call and init method it retains, bringing the retain count to 2.
It also releases when you reassign it so you can just
self.dataList = newValue;
#syntehsize'd properties take care of all the retain release stuff for you.
Related
I having a warning when i analyze my code for memory leak from xcode. I tried for hour to figure it out but couldn't understand, but i have some suspicion on specific code:
[stack push:[outputString copy]];
[stack print]; //here xcode tell there is potential leak of an object that is createn on above line
and my stack implementation is:
-(void) push:(NSString *)element{
[store addObject:element];
top++;
}
-(void) print{
NSLog(#"%#", [store objectAtIndex:top]);
}
- (void)dealloc {
[store release];
[super dealloc];
}
and this is my init for my stack class:
-(id) initWithMutableArray{
self = [super init];
if(self){
store = [[NSMutableArray alloc] init];
top = -1;
}
return self;
}
My suspicion is on code [outputString copy]. But i am storing it on array and i am releasing store array on dealloc.
Gracias.
There is no release to balance the [outputString copy] so it is leaked. When adding the object to your array it gets retained by the array and released when the array is destroyed.
I get leaks if I dont put it in dealloc. I get a crash EXC_BAD_ACCESS If I do. I cannot see anything wrong with this code. The bad access is pointed at [events release]. Have I made a mistake in the code below or is Instruments just having a laugh at my expense?
events is an NSArray
#interface EventsViewController : UITableViewController
{
#private
NSArray *events;
}
- (void)viewDidLoad
{
events = [[self getEvents] retain];
}
- (void)dealloc
{
[events release];
[super dealloc];
}
- (NSArray*)getEvents
{
NSMutableArray *response = [[[NSMutableArray alloc] init] autorelease];
//Some sql
while(sqlite3_step(statement) == SQLITE_ROW)
{
Event *event = [[[Event alloc] init] autorelease];
event.subject = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
[response addObject:event];
}
return response;
}
Update
A lot of you are saying the code is fine which is a plus. I dont manipulate events elsewhere - I have removed any code that does to try and single out the crash. Perhaps its in the parent view?
This is the click event that pushes the EventsViewController:
- (void)eventsClick:(id)sender
{
EventsViewController *eventsViewController = [[EventsViewController alloc] initWithNibName:#"EventsViewController" bundle:nil];
eventsViewController.anywhereConnection = anywhereConnection;
eventsViewController.contact = contact;
[[self navigationController] pushViewController:eventsViewController animated:YES];
[eventsViewController release];
}
The crash is actually happening when I return to the parent view. (I think it is considered a parent in this scenario). But perhaps the [eventsViewController release] just triggers dealloc in the EventViewController.
Have you considered just refactoring your code to use ARC? It works with iOS 4 and up and will make your life a lot easier. There are plenty of tutorials out there that will guide you how to do it, and will remove the need to manually figure out the nuances of memory management.
If your Events object has property 'subject' set as assign, then the results of stringWithUTF8String: will not be retained. (Same thing if Events is a C++ object.)
The stringWithUTF8String: method returns an auto-released object that will be released at the next turn of the event loop.
There is a huge difference when you reference a variable via "self", and when you don't.
When you use
events = [[self getEvents] retain];
the memory allocated in getEvents never gets stored in the class property and is basically a leak.
You need to use
self.events = [self getEvents]; // no need to use retain if property is correctly defined.
Then
[events release];
should work fine.
try putting
events = nil;
in dealloc.
I am releasing NSArray and NSMutableArray but its show memory leak. while ZoneData code is like this
-(ZoneData*) initWithZoneName:(NSString *)zoneNameIn SdName:(NSString *)sdNameIn eCount:(NSString *)eCountIn iCount:(NSString *)iCountIn StandLat:(NSString *)standLatIn StandLong:(NSString *)standLongIn
{
self = [super init];
if (self)
{
zoneName = [zoneNameIn copy];
lsdName = [sdNameIn copy];
leCount = [eCountIn intValue];
liCount = [iCountIn intValue];
standLat = [standLatIn copy];
standLong = [standLongIn copy];
}
return self;
}
how to solve this?
The problem is your instance variables. In your -init, you are correctly assigning them to copies of the strings from the array. However, you need t also release them in -dealloc.
-(void) dealloc
{
[zoneName release];
[lsdName release];
[standLat release];
[standLong release];
[super dealloc];
}
Now, you may be asking why the leaks tool is telling you the leaks are where you create the NSArray with the strings in it instead of the init method. The reason is that -copy for immutable objects is optimised to do nothing except send retain to self. So those copies you have as instance variables are in reality the same objects as was created by -componentsSeparatedByString:
componentsSeparatedByString: returns an autoreleased NSArray. You are not supposed to release that yourself, but the closest NSAutoreleasePool will do that for you. In line 61 you are overreleasing the array.
If you are concerned about the memory usage while performing the loop you can clear autoreleased objects in each iteration of the loop:
for (...)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// your loop contents.
[pool drain];
}
I am using the code below to set my two NSArray ivars:
The issue is, I keep getting a memory leak on the following lines:
followingFriendsArray = [[NSArray alloc] initWithArray:friend.Following];
followerFriendsArray = [[NSArray alloc] initWithArray:friend.Followers];
Is this not the correct way to set ivars from an existing NSArray of items? Any help would be appreciated. I've also tried to autorelease the above two lines, but when I actually access them in another method I get an error that they've already been released.
I have included my Interface and Implementation code below:
Interface .h:
NSArray *followingFriendsArray;
NSArray *followerFriendsArray;
#property (nonatomic, retain) NSArray *followingFriendsArray;
#property (nonatomic, retain) NSArray *followerFriendsArray;
Implementation .m:
- (void)handlerGetFollowingInformation:(id)value {
BOOL success = [Utility checkWebServiceErrors:value controller:self.navigationController];
if (success) {
Friend *friend = (Friend *)value;
followingFriendsArray = [[NSArray alloc] initWithArray:friend.Following];
followerFriendsArray = [[NSArray alloc] initWithArray:friend.Followers];
}
}
This is how I need to access the arrays:
- (void)followersButtonTapped:(id)sender {
FollowingVC *fvc = [[FollowingVC alloc] initWithNibName:#"FollowingViewController" bundle:nil];
fvc.friends = followerFriendsArray;
[self.navigationController pushViewController:fvc animated:YES];
[fvc release];
}
I release my two ivars in the following way as per usual:
- (void)viewDidUnload {
self.followingFriendsArray = nil;
self.followerFriendsArray = nil;
[super viewDidUnload];
}
- (void)dealloc {
[followingFriendsArray release];
[followerFriendsArray release];
[super dealloc];
}
I mean the code works just fine, it's just that I'm concerned about said memory leaks when I run the "Leaks" performance tool.
OK
you should not use autorelease in this case, but you have to release the arrays by calling :
[followingFriendsArray release];
[followerFriendsArray release];
you can do it:
when you don't need to use them any more.
in the dealloc method in your .m file.
option 2looks like that -
- (void)dealloc {
[followingFriendsArray release];
[followerFriendsArray release];
[super dealloc];
}
BTW -
if you don't manipulate the arrays after creating them (add / remove objects) you should use an immutable array (NSArray).
Good Luck
Your method handlerGetFollowingInformation is assigning new values to followingFriendsArray and followerFriendsArray without releasing the previous contents. If you call this method more than once on the same instance you will leak.
CRD is right that the arrays are not released inside the handlerGeFollowingInformation method but the fix is maybe overkill. What you need to do is to use self. so that the setter method is called which does that automatically. You could should look like this:
- (void)handlerGetFollowingInformation:(id)value {
BOOL success = [Utility checkWebServiceErrors:value controller:self.navigationController];
if (success) {
Friend *friend = (Friend *)value;
self.followingFriendsArray = [[NSArray alloc] initWithArray:friend.Following];
self.followerFriendsArray = [[NSArray alloc] initWithArray:friend.Followers];
}
}
Easy fix but hard to spot and I ran into this issue over and over again especially when I started to dealloc are the properties.
-Andy
I have numerous classes that use the various NSDictionary/NSArray collection classes as ivars but often I run into the problem of my collection class getting released before the containing class is released.
This seems to happen mostly with the collections classes and not with another model class (ie classes that I either created separately or other NS* non-collection classes).
Here are the two variations I've done and seen other people do:
#implementation ClassX
// myDictionary declared as a property in the .h file as this:
// #property (nonatomic, retain) NSMutableDictionary *myDictionary;
#synthesize myDictionary;
- (id)int
{
if (self = [super init])
{
// Option 1:
// If I don't instantiate and assign with 'self',
// myDictionary ivar will not be available
// at times in doSomething.
myDictionary = [NSMutableDictionary dictionary];
// Option 2:
// Doing this, however will keep the dictionary around.
// because I have invoked an extra retain on the dictionary
self.myDictionary = [NSMutableDictionary dictionary];
// Which one is more correct?
}
return self;
}
- (void)doSomething
{
// this will give the error about trying to invoke
// a method on an already released instance
[myDictionary objectForKey:#"myKey"];
}
- (void)dealloc
{
// If I did self.myDictionary in 'init', I then
// need to do this:
[myDictionary release];
[super dealloc];
}
#end
So which approach is the more correct way to hold an instance of NSDictionary within a class?
Option 2 is correct; Option 1 is wrong.
But you left out the best option: myDictionary = [[NSMutableDictionary alloc] init].
I recommend using
myDictionary = [[NSMutableDictionary alloc] init];
The memory is only within the scope of the method you're in if you call [NSMutableDictionary dictionary]. Once you leave the method, that memory goes with it which is why you need to alloc/init if you want to retain the values.
That's why you don't have to release if you don't encounter an alloc.
So for instance:
- (void) doSomething {
// Do not need to release this string
NSString *someText = #"Hello world!";
// You need to release this string:
NSString *otherText = [[NSString alloc] initWithString:#"Hello world!"];
[otherText release];
}
Edited: Removed self after #mipadi #st3fan and caught my mistake. Forgot to post the change. Thanks for keeping me accountable.