iPhoneOS 3.2
I use NSKeyedUnarchiver's unarchiveObjectWithFile: to load a custom object that contains a single large NSData and another much smaller object. The dealloc method in my custom object gets called, the NSData object is released, its retainCount == 1 just before. Physical memory does not decrement by any amount, let alone a fraction of the NSData size, and with repetition memory warnings are reliably generated: I have test until I actually received level 2 warnings. =(
NSString *archivePath = [[[NSBundle mainBundle] pathForResource:#"lingering"]
ofType:#"data"] retain];
lingeringDataContainer = [[NSKeyedUnarchiver unarchiveObjectWithFile:archivePath] retain];
[archivePath release];
[lingeringDataContainer release];
and now the dealloc....
- (void) dealloc {
[releasingObject release];
[lingeringData release];
[super dealloc];
}
Before release:
(gdb) p (int) [(NSData *) lingeringData retainCount]
$1 = 1
After:
(gdb) p (int) [(NSData *) lingeringData retainCount]
Target does not respond to this message selector.
First, you're retaining and releasing objects which do not need to have that happen to them. Here's the cleaned up code:
NSString *archivePath = [[NSBundle mainBundle] pathForResource:#"lingering"]
ofType:#"data"]; // Do not retain again.
lingeringDataContainer = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath]; // Do not retain again.
// Do not release, because archivePath is already autoreleaed: [archivePath release];
// Again, this is already autoreleased: [lingeringDataContainer release];
Or more simply:
NSString *archivePath = [[NSBundle mainBundle] pathForResource:#"lingering"]
ofType:#"data"];
lingeringDataContainer = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];
Second, where's the rest of the code? It's probably something else which is being retained or cached somewhere else.
When are you checking the memory usage? Is it just after the code snippet you posted? archivePath and lingeringDataContainer are both autoreleased. They will not be deallocated until (at least) the autorelease pool is drained (usually at the end of the current event). There may also be a lot of other internal stuff that has been autoreleased and won't go away until the pool is drained.
Try doing this:
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; // <==
NSString *archivePath = [[[NSBundle mainBundle] pathForResource:#"lingering"]
ofType:#"data"] retain];
lingeringDataContainer = [[NSKeyedUnarchiver unarchiveObjectWithFile:archivePath] retain];
[pool release]; // <==
[archivePath release];
[lingeringDataContainer release];
Related
I have 100 images in my resource bundle named like image1.jpg,image2.jpg.
Basically what i am trying to do is create path names to those images dynamically inside a for loop.
While testing in simulator,the images loaded fine and the app did not crash.But while testing the app with instruments i was shocked to see the heavy memory leak that was happening while i was creating the path1 object.
I am pasting the entire method here for reference
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
for(int i=1 ; i<100 ; i++){
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSBundle mainBundle] pathForResource:str ofType:#"jpg"];
[self.arrayImages addObject:path1];
}
}
return self;
}
As i have not made use of any alloc inside the loop i dont have any ownership and hence no right to release the object.What is the reason for this memory leak??
Kindly explain the problem and provide the necessary solution in order to fix it..
As always,any help is highly appreciated..
arrayImages is retaining path1, and so if you do not release arrayImages it will leak. How are you creating arrayImages, and are you releasing it anywhere?
Edited based on comments:
Make sure you release arrayImages in your -dealloc method like so: [arrayImages release]; (note the lack of self).
There is no leak in the code you've shown.
There are (at least) two possibilities:
You have a leak in code you didn't paste into your question
Everything is fine and Instruments gave you a false-positive
Your loop will create a lot of autoreleased variables. These won't be deallocated until after the loop has finished, but that's how it's supposed to work.
The reason for the leak would be this line right here:
NSString *str = [NSString stringWithFormat:#"Century%d",i];
By using convenience methods in Objective-C, what happens in the background is the following:
NSString *str = [[[NSString alloc] initWithFormat:#"Century%d", i] autorelease];
Not using alloc/init to create a weak reference is a misconception. You are always the owner of a created object, no matter how you create it. The convenience method simply does the alloc/init and autoreleases it for you.
Here's what I would suggest you do to avoid leaking memory:
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
NSAutoreleasePool *tmpPool = [[NSAutoreleasePool alloc] init];
for(int i = 1 ; i < 100 ; i++) {
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSString alloc] initWithString:[[NSBundle mainBundle] pathForResource:str ofType:#"jpg"]];
[self.arrayImages addObject:path1];
[path1 release];
}
[tmpPool drain];
}
return self;
}
Let me know if this works better for you.
-EDIT- Allocating the path1 object and releasing it after adding to arrayImages.
Hi I generally create objects of another classes. can you please tel me if this wil be in the auto release pool? or should we release it manually.
if you init copy or new them you'll have to deallocate them if you put an autorlease with the allocation then they will be autoreleased
for example
Foo *foo = [[Foo alloc] init]; //you'll have release it somewhere yourself
And
Foo *foo = [[[Foo alloc] init] autorelease];// this will be autreleased
The simple case is : if you use init, you are responsible for releasing it, either by calling release or by calling autorelease.
e.g.
NSString *myString = [NSString alloc] init]; // You need to release this
...
[myString release]; // Now it's released - don't use it again!
or if you are going give it to someone else
NSString *myString = [NSString alloc] init]; // This needs releasing
...
return [myString autorelease]; // You are finished with it but someone else might want it
However, there's a few other cases.
NSString *myString = [NSString stringWithFormat:#"hi"];
This object is in the autorelease pool already - don't release it!
NSString *secondString = [myString copy];
This object needs releasing - it is not autoreleased.
Rule of thumb : Anything with init, copy or new in the name - you made it, you release it. Anything else will be autoreleased.
I want to get some memory leaks in my code, how can i fix the memory leak.
dashboard = [[NSMutableArray alloc] init];
[dashboard addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"demo_1.jpg",#"pic_source",#" Head",#"Title",nil]; // memory leaks here.
if ( theConnection ) {
receiveData = [[NSMutableData data] retain]; //memory leaks here.
}
But i have released dealloc - in[receiveData release];, but memory leaks happened. I know the retain, the count is increased, but how can i released the data properly.
Thanks!
i think your leak in the line:
[dashboard addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"demo_1.jpg",#"pic_source",#" Head",#"Title",nil];
just change it to
[dashboard addObject:[NSDictionary dictionaryWithObjectsAndKeys:#"demo_1.jpg",#"pic_source",#" Head",#"Title",nil];
addObject retains the object so you can use convenience creation methods which autorelease the object they create.
and another possible leak... if you define your receiveData as a property with retain attribute you don't need to call retain explicitely. You can call self.recieveData = [NSMutableData data]. This will retain it. Of course you will still need to release it in dealloc.
EDIT to show the code:
NSMutableArray *anArray = [[[NSMutableArray alloc] init] autorelease];
[sections setValue:anArray forKey:display_date];
dashboard addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"demo_1.jpg",#"pic_source",#" Head",#"Title",nil];
You've called init method - then you are the owner of an object. When you've put it to the array - the retain was called too. Just call autorelease to fix the leak.
receiveData = [[NSMutableData data] retain]; - if you will call release this object will be deleted when out of scope. If you will write
receiveData = [NSMutableData data];
It will be automatically deleted when out of scope
EDIT
If you are using methods with init word in them then you are the owner of such objects and they will not be deleted automatically until you'll call release method on them. If you don't want to own the object create it with some static method. For example:
NSMutableArray *array = [NSMutableArray arrayWithObjects: ... , nil];
It's the same as calling
NSMutableArray *array = [[[NSMutableArray alloc] initWithObjects: ... , nil] autorelease];
Autorelease means the object will receive a release method when out of scope - and if it was not retained will be deleted automatically
i been cracking my head over this memory leak..
my datasource is mutabledictionary..that i load in the viewdidload. if i dont retain it. i dont have access it it in cellforrowatindexpath. but when i retain it.. it shows up as a memory leak in instruments. i have tried so many different variations.. doesnt seem to get it right.
here is the code the leak is in "dict" and "plistPath"
`
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.navigationBarHidden = NO;
self.title = #"Messages & Lists";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[plistPath release];
plistPath = [documentsDirectory stringByAppendingPathComponent:#"general.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[dict release];
if ( [fileManager fileExistsAtPath:plistPath] ) {
dict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath] ;
} else {
dict = [NSMutableDictionary dictionaryWithCapacity:1];
[dict setObject:#"NO" forKey:#"busyStatus"];
[dict setObject:#"NO" forKey:#"replyToAll"];
[dict setObject:#"NO" forKey:#"replyToList"];
[dict setObject:#"NO" forKey:#"dontReplyToList"];
[dict writeToFile:plistPath atomically:YES];
}
[tableData release];
tableData = [[NSMutableDictionary alloc] init];
[tableData setObject:[NSArray arrayWithObjects:#"Help",#"Set Default Message",#"Reply To All",[dict objectForKey:#"replyToAll"],nil] forKey:#"1"];
[tableData setObject:[NSArray arrayWithObjects:#"Reply to a List",[dict objectForKey:#"replyToList"],#"List of Contacts",nil] forKey:#"2"];
[tableData setObject:[NSArray arrayWithObjects:#"Don't reply to List",[dict objectForKey:#"dontReplyToList"],#"List of Contacts",nil] forKey:#"3"];
[dict retain];
[plistPath retain];
}
`
there is no leak the first time the view loads. but if i got back. and then load the view again it leaks.
thanks in advance for anyone who can help me out.
You have to call [dict release] in your view controller's dealloc method.
I think you should release your dictionary in the dealloc method of your view controller so that the retain count of your datasource is decreased when the ViewController is deallocated.
Retaining without releasing after use is the main source of memory leak.
It seems right.
The only thing that confuses me is the tableData. Is that var released or allocated somewhere else too?
Say you do allocate it somewhere else, and not releases it in dealloc, shouldn't these be the steps then:
tableData is allocated somewhere else.
tableData is released in viewDidLoad
tableData is allocated again (counter 1).
Exiting view, entering view again, tableData allocated again, released and allocated in function (counter two?)
Seems like a longshot, but. Can you display what the instruments say?
I have been living on Instruments for last few hours staring at a puzzling memory leak. I have isolated it to this single line of code in an NSOperation subclass I wrote:
NSData *myData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:myURLString]];
Periodically this will leak 3500 bytes. Is anyone else seeing this? If so, is there a work around?
Thanks in advance.
UPDATE:
Here is the relevant section of code within the main() body of my NSOperation subclass:
- (void)main {
// ...
NSData *sequenceData =
[[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:concatenatedURLString]];
NSString *sequenceString =
[[NSString alloc] initWithBytes:[sequenceData bytes] length:[sequenceData length] encoding:NSUTF8StringEncoding];
NSDictionary *result = [NSDictionary dictionaryWithObjectsAndKeys:
self.chromosome, #"chromosome",
[NSNumber numberWithInt:self.basepairStart], #"basepairStart",
[NSNumber numberWithInt:self.basepairEnd], #"basepairEnd",
sequenceData, #"sequenceData",
sequenceString, #"sequenceString",
nil];
[sequenceData release];
[sequenceString release];
[self.target performSelectorOnMainThread:self.action withObject:result waitUntilDone:NO];
}
As you can see sequenceData and sequenceString are properly released. Also, I have confirmed that all ivars of this subclass (chromosome. etc.) are properly memory managed.
-Doug
You have to release or autorelease myData, otherwise it will leak according to the Cocoa Memory Management Rules