I thought I knew how to deal with memory leaks and arrays, but then this pops up. I can't figure pout why this is leaking:
// MyViewController.h
NSMutableArray *myMutableArray;
#property (nonatomic, retain) NSMutableArray *myMutableArray;
// MyViewController.m
#synthesize myMutableArray;
- (void) viewDidLoad {
if (self.myMutableArray == nil) {
self.myMutableArray = [[NSMutableArray alloc] init];
}
. . .
for (NSUInteger i = 0; i < someCount; ++i) {
[self.myMutableArray addObject:[NSString stringWithFormat: #"%#",myString]];
}
}
- (void)viewDidUnload {
self.myMutableArray = nil
}
- (void)dealloc {
[myMutableArray release];
}
Your problem is here:
if (self.myMutableArray == nil) {
self.myMutableArray = [[NSMutableArray alloc] init];
}
It should be:
if (myMutableArray == nil) {
self.myMutableArray = [[[NSMutableArray alloc] init] autorelease];
}
Or:
if (myMutableArray == nil) {
myMutableArray = [[NSMutableArray alloc] init];
}
Explanation:
Since you are using retain as a property mutator attribute, the object will be retained when it is passed to the property setter, therefore you have a leak when you retain an object you already have ownership of.
The solution to this is to either a) Pass an autorelease-d object to the property setter or b) Assign the ivar directly to the alloc-ed object.
You're allocating a new array, then setting it to a retain property. Change that line to
self.myMutableArray = [NSMutableArray array];
self.myMutableArray = [[NSMutableArray alloc] init];
should be
self.myMutableArray = [[[NSMutableArray alloc] init] autorelease];
because myMutableArray is a retained property.
Related
I want to build a shared object that all the classes will be able to access to it.
i want that in this object will be NSMutableArray .
this is how i call this object
+(id)sharedManager{
#synchronized(self) {
if (sharedManager == nil){
sharedManager = [[self alloc] init];
array = [[NSMutableArray alloc] init];
}
}
return sharedManager;
}
and this is how i define the NSMutableArray :
#property (nonatomic,retain) NSMutableArray *array;
the problem is that after i create this NSMutableArray in the sharedManager method, every time i try to access the array is equal to Nil.
You're attempting to set an instance variable from a class method. Instead, you should create array in your -init method in the singleton. That way when you message, sharedManager = [[self alloc] init];, the array will be configured for that shared instance.
- (id)init
{
self = [super init];
if (!self)
return nil;
array = [[NSMutableArray alloc] init];
return self;
}
I want to populate an array like this:
NSMutableArray *array = [self methodThatReturnsAnArray];
In the "methodThatReturnsAnArray"-method I create an array like this:
NSMutableArray *arrayInMethod = [[NSMutableArray alloc] init];
When I'm finished populating "arrayInMethod" I'm returning the array and in order to prevent a memory leak I'm using:
return [arrayInMethod autorelease];
However the "array"-variable is never populated. When removing the "autorelease" it works fine though. What should I do in order to make sure that the returned object i released?
EDIT
+ (NSMutableArray *)buildInstants:(NSArray *)huntsArray {
NSMutableArray *goGetObjects = [[[NSMutableArray alloc] init] autorelease];
for (int i = 0; i < [huntsArray count]; i++) {
NSDictionary *huntDict = [huntsArray objectAtIndex:i];
PHGoGet *goGet = [[PHGoGet alloc] init];
goGet.title = [huntDict objectForKey:#"title"];
goGet.description = [huntDict objectForKey:#"description"];
goGet.start = [huntDict objectForKey:#"start"];
goGet.end = [huntDict objectForKey:#"end"];
goGet.ident = [huntDict objectForKey:#"id"];
if ((CFNullRef)[huntDict objectForKey:#"image_url"] != kCFNull) {
goGet.imageURL = [huntDict objectForKey:#"image_url"];
} else {
goGet.imageURL = nil;
}
if ((CFNullRef)[huntDict objectForKey:#"icon_url"] != kCFNull) {
goGet.iconURL = [huntDict objectForKey:#"icon_url"];
} else {
goGet.iconURL = nil;
}
goGet.longitude = [huntDict objectForKey:#"lng"];
goGet.latitude = [huntDict objectForKey:#"lat"];
goGet.companyIdent = [huntDict objectForKey:#"company_id"];
[goGetObjects insertObject:goGet atIndex:i];
[goGet release];
}
return [[goGetObjects copy] autorelease];
}
Try using the convienence method for NSMutableArray... change:
NSMutableArray *arrayInMethod = [[NSMutableArray alloc] init];
To...
NSMutableArray *arrayInMethod = [NSMutableArray array];
array will return an autoreleased object.
First of all, I recommend you not to return a NSMutableArray from any method. It's better to use NSArray for that to avoid some very difficult to debug problems. My proposition is:
You declare the mutable array and populate it:
NSMutableArray *arrayInMethod = [[[NSMutableArray alloc] init] autorelease];
Then you return an autoreleased copy:
return [[arrayInMethod copy] autorelease];
And finally, when you take the returned array, you make it mutable again (only if you need to change it):
NSMutableArray *array = [[self methodThatReturnsAnArray] mutableCopy];
When you're done with the array, you release it:
[array release];
I have written class and want show it to you ...
I think that this class written not correct and thats why I have leeks in my application. First of all the delloc never calls. What can I change in this class to make it better, please help.
Articles.h
#import <Foundation/Foundation.h>
#interface Article : NSObject {
BOOL favorite;
NSMutableString * title;
NSMutableString * summary;
NSMutableString * mainLink;
NSMutableString * pubDate;
NSMutableString * author;
NSMutableString * imageLink;
}
#property (nonatomic, assign) BOOL favorite;
#property (nonatomic, retain) NSMutableString * title;
#property (nonatomic, retain) NSMutableString * summary;
#property (nonatomic, retain) NSMutableString * mainLink;
#property (nonatomic, retain) NSMutableString * pubDate;
#property (nonatomic, retain) NSMutableString * author;
#property (nonatomic, retain) NSMutableString * imageLink;
- (id)initWithValues:(NSString *) inTitle mainLink:(NSString *) inMainLink summary:(NSString *) inSummary
pubDate:(NSString *) inPubDate author:(NSString *) inAuthor imageLink:(NSString *) inImageLink;
//Setter methods
- (void)setTheTitle:(NSString *) inTitle;
- (void)setTheMainLink:(NSString *) inMainLink;
- (void)setTheSummary:(NSString *) inSummary;
- (void)setThePubDate:(NSString *) inPubDate;
- (void)setTheAuthor:(NSString *) inAuthor;
- (void)setTheImageLink:(NSString *)inImageLink;
#end
Articles.m
#import "Articles.h"
#implementation Article
#synthesize favorite;
#synthesize title;
#synthesize summary;
#synthesize mainLink;
#synthesize pubDate;
#synthesize author;
#synthesize imageLink;
- (void)dealloc {
NSLog(#"article dealloc \n");
[self.title release];
[self.mainLink release];
[self.summary release];
[self.pubDate release];
[self.author release];
[self.imageLink release];
[super dealloc];
}
- (id)init {
self = [super init];
if(self) {
// set your properties...
self.title = [[[NSMutableString alloc] init] autorelease];
self.mainLink = [[[NSMutableString alloc] init] autorelease];
self.summary = [[[NSMutableString alloc] init] autorelease];
self.pubDate = [[[NSMutableString alloc] init] autorelease];
self.author = [[[NSMutableString alloc] init] autorelease];
self.imageLink = [[[NSMutableString alloc] init] autorelease];
self.favorite = NO;
}
return self;
}
- (id)initWithValues:(NSString *) inTitle mainLink:(NSString *) inMainLink summary:(NSString *) inSummary
pubDate:(NSString *) inPubDate author:(NSString *) inAuthor imageLink:(NSString *) inImageLink
{
self = [super init];
if(self) {
// set your properties ...
if (inTitle != nil) {
self.title = inTitle;
}
if (inMainLink != nil) {
self.mainLink = inMainLink ;
}
if (inSummary != nil) {
self.summary = inSummary;
}
if (inPubDate != nil) {
self.pubDate = inPubDate;
}
if (inAuthor != nil) {
self.author = inAuthor ;
}
if (inImageLink != nil) {
self.imageLink = inImageLink ;
}
self.favorite = NO;
}
return self;
}
#end
ADDED:
Look I have NSXMLParser in my main class. In the main class .h file I write:
Article * currentArticle;
Now In .m file when parser didStartElement I alloc ant initialize Article in parser didEndElement I release it [self.currentArticle release]; but delloc not calles.
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
// Copy current Xml Element name.
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
// Clear out our story item caches...
self.currentArticle = [[Article alloc] init];
}
[currentElement release];
}
ADDED RELEASE FOR TEST
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
// Copy current Xml Element name.
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
// Clear out our story item caches...
self.currentArticle = [[Article alloc] init];
[self.currentArticle release];
}
[currentElement release];
}
look I have added [self.currentArticle release]; right after initialization and put breakpoint here ... When at first time my application enters in this part of code it call init but not call release at second time it call release ? But why ? It's not right
WHY I DON'T USE AUTORELEASE !!!
self.title = [[[NSMutableString alloc] init] autorelease];
self.mainLink = [[[NSMutableString alloc] init] autorelease];
self.summary = [[[NSMutableString alloc] init] autorelease];
self.pubDate = [[[NSMutableString alloc] init] autorelease];
self.author = [[[NSMutableString alloc] init] autorelease];
self.imageLink = [[[NSMutableString alloc] init] autorelease];
I do not use autorelease in this part of code because I have read that it brings to leeks, because when I write autorelease the objects releases in the end of application work ! Am I write ???
Thanks !!!
You can remove every setter like
- (void)setTheSummary:(NSString *) inSummary
{
if (self.summary != nil) {
[self.summary release];
}
self.summary = [[NSMutableString alloc] initWithString:inSummary];
}
You've declared every ivar as property already and synthesized getters and setters. So you can set the title for example with:
self.title = newTitle;
This will retatin the newTitle and assign it to title and release the previous(if present) value.
EDIT
If you set properties like
self.title = [[NSMutableString alloc] init];
the instande of the mutable string will get over retained, so there will be leaks.
Retain increases the retain count by 1, this happens through the property's declaration and it will go one up by calling init.
Change it to:
self.title = [[[NSMutableString alloc] init] autorelease];
EDIT 2
Change your initialization of these constructs:
if (inTitle == nil) {
self.title = [[NSMutableString alloc] init];
}
else
{
[self.title release];
self.title = [[NSMutableString alloc] initWithString:inTitle];
}
To:
if (inTitle != nil) {
self.title = inTitle;
}
Now add
self = [self init];
and remove
[super init];
at the beginning of your init method initWithValues; This will initialize the properties for you first, it reduces code duplication and makes your class smaller. The removal of [super init] is necessary to call the the initializer of NSObject only once, you are doing this by calling self = [self init];.
You've created with this pattern a so called designated initializer. You can read more on initializers here.
EDIT 3
To make your intializers perfect you should write them this way:
- (id)init
{
self = [super init];
if(self) {
// set your properties...
}
return self;
}
and
- (id)initWithValues:(NSString *) inTitle mainLink:(NSString *) inMainLink summary:(NSString *) inSummary
pubDate:(NSString *) inPubDate author:(NSString *) inAuthor imageLink:(NSString *) inImageLink
{
self = [self init];
if(self) {
// set properties with parameters ...
}
return self;
}
This pattern will allow you to react on errors which can occur in your designated initializer and/or in the initializers called up the inheritance hierarchy. This will ensure that a nil is returned if something goes wrong and you don't set properties to an erroneous instance.
For starters I would replace every
if (self.imageLink !=nil) {
[self.imageLink release];
self.imageLink = nil;
}
in dealloc with
[imageLink release];
To help you fix the major issue, that is, why your instance never gets deallocated, we need to see the code where you create it.
Edit
Ok, here is a guess what might be happening. I hope you can use this guess to fix the issue (without seeing the actual code this is all I can do):
Somewhere in you code you have something like this:
Article *article = [[Article alloc] init];
You say you are calling release on the article so you will also have
[article release];
somewhere else in you code.
Now why is dealloc not called even though you call release? My guess is that you are doing something like this in between:
NSMutableArray *array = [NSMutableArray arrayWithCapacity: 0];
[array addObject: article];
This actually retains article and could be the reason why it never gets released. How to fix? Like this:
[array removeObject: article];
Hope this helps.
While there are numerous things going wrong with your code, this is the part which causes memory leaks:
self.title = [[NSMutableString alloc] init];
Why?
The title property is defined as:
#property (nonatomic, retain) NSMutableString * title;
Hence, the setter increases the retain count to 2.
In dealloc retain count for title is only decreased by one, so the object is still alive after deallocing the article.
Quick fix:
self.title = [[[NSMutableString alloc] init] autorelease];
Edit: this is why dealloc never gets called:
self.currentArticle = [[Article alloc] init];
Again, take a look at the retain count: alloc sets retain count to 1.
Then - I assume the currentArticle is defined as (nonatomic, retain) - it is retained again, retain count is now 2. In the dealloc method - I assume again - you release currentArticle, so retain count is 1. An object won't dealloc unless retain count is 0!
Quick fix: same as above
self.currentArticle = [[[Article alloc] init] autorelease];
I'm puzzled about iOS memory management. I have a class that has a member of type NSMutableArray. When storing objects of this type in another array and removing them, Instruments shows that all those members leak memory. Here's the definition of my rogue class:
#interface Tester : NSObject {
int some;
NSMutableArray* others;
}
#property int some;
#property (nonatomic, retain) NSMutableArray* others;
-(id)init;
-(id)copy;
-(void)dealloc;
#end
Here's the implementation of the rogue class:
#implementation Tester
#synthesize some;
#synthesize others;
-(id)init {
self = [super init];
if(self) {
some = 0;
others = [[NSMutableArray alloc] initWithCapacity:5];
int i;
for(i = 0; i < 5; ++i) {
[others addObject:[NSNumber numberWithInt:i]];
}
}
return self;
}
-(id)copy {
Tester* cop = [[Tester alloc] init];
cop.some = some;
cop.others = [others mutableCopy]
return cop;
}
-(void)dealloc {
[others removeAllObjects];
[others release];
[super dealloc];
}
#end
And here's how I test it:
NSMutableArray* container = [[NSMutableArray alloc] init];
Tester* orig = [[Tester alloc] init];
int i;
for(i = 0; i < 10000; ++i) {
Tester* cop = [orig copy];
[container addObject:cop];
}
while([container count] > 0) {
[[container lastObject] release];
[container removeLastObject];
}
[container release];
Running this code leaks memory and Instruments shows that the leaked memory is allocated at line:
cop.others = [others mutableCopy];
What have I done wrong?
You are creating a copy: [others mutableCopy] which you own but forget to release. The line should be:
cop.others = [[others mutableCopy] autorelease];
You testing code would be clearer if you'd let the container array be the sole owner of the Tester objects:
NSMutableArray* container = [[NSMutableArray alloc] init];
Tester* orig = [[Tester alloc] init];
for (int i = 0; i < 10000; ++i)
[container addObject:[[orig copy] autorelease]];
while([container count] > 0)
[container removeLastObject];
[container release];
Now you could remove the loop that empties the container.
Or you could just skip the container:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Tester* orig = [[[Tester alloc] init] autorelease];
for (int i = 0; i < 10000; ++i)
[[orig copy] autorelease];
[pool drain]; // At this point all Tester objects are released
cop.others = [others mutableCopy]
Others is declared as a retained property, so assigning to it establishes an ownership claim on the new value. -mutableCopy is a method that implies ownership (because it contains the word "copy"). So you now have two claims of ownership, both of which must be released. The recommended way to do this is to assign the copy first to a temp variable, then assign that to your property and release it, like so:
NSMutableArray *tmpArray = [others mutableCopy];
cop.others = tmpArray;
[tmpArray release];
You could also do this in one step, avoiding the temp object, although doing so uses the autorelease pool and is slightly less efficient because of that:
cop.others = [[others mutableCopy] autorelease];
My coding contains a memory leak, and somehow I can't find the leak.
Leaks points me in the direction of the way I create "ReportDetailItems"
e.g. areaContainer = [[[ReportDetailItem alloc] init] autorelease];
I've been looking at this for hours and I am at a total loss, the objects reported leaking are "ReportDetailItem", and the NSMutableDictionary contained in those objects.
Please advice.
------[ReportDetailItem.h
#interface ReportDetailItem : NSObject
{
NSNumber *total;
NSMutableDictionary *items;
}
#property (nonatomic, retain) NSNumber *total;
#property (nonatomic, retain) NSMutableDictionary *items;
- (NSString *)description;
#end
------[ReportDetailItem.m
#synthesize items, total;
- (id)init {
if (self = [super init]) {
self.items = [NSMutableDictionary dictionaryWithCapacity:0];
DLog("Alloc: %d", [items retainCount]);
}
return self;
}
- (NSString *)description {
return #"ReportDetailItem";
}
- (void)release {
[super release];
}
- (void)dealloc {
[self.items release];
[self.total release];
items = nil;
total = nil;
[super dealloc];
}
#end
------[Leaking code
NSError *error;
NSArray *data = [self.managedObjectContext executeFetchRequest:request error:&error];
if (data == nil || [data count] == 0) {
DLog(#"No data.")
} else {
for (int i=0; i < [data count]; i++) {
TaskEntity *task = [data objectAtIndex:i];
NSString *areaKey = task.activity.project.area.title.text;
NSString *projectKey = task.activity.project.title.text;
NSString *activityKey = task.activity.title.text;
ReportDetailItem *areaContainer;
if (![dataSource objectForKey:areaKey]) {
areaContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
areaContainer = [dataSource objectForKey:areaKey];
}
areaContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [areaContainer.total intValue])];
[dataSource setObject:areaContainer forKey:areaKey];
ReportDetailItem *projectContainer;
if (![areaContainer.items objectForKey:projectKey]) {
projectContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
projectContainer = [areaContainer.items objectForKey:projectKey];
}
projectContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [projectContainer.total intValue])];
[areaContainer.items setObject:projectContainer forKey:projectKey];
ReportDetailItem *activityContainer;
if (![projectContainer.items objectForKey:activityKey]) {
activityContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
activityContainer = [projectContainer.items objectForKey:activityKey];
}
activityContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [activityContainer.total intValue])];
[projectContainer.items setObject:activityContainer forKey:activityKey];
}
}
I found it, the leak was located in the way I allocated the "dataSource"
---[Leak
- (void)viewDidLoad {
[super viewDidLoad];
self.dataSource = [[NSMutableDictionary alloc] init];
[self fetchData];
}
---[No leak
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
self.dataSource = dict;
[dict release];
[self fetchData];
}
I'm pretty skeptic about the two ways u assign pointers to the ReportDetailItem. Why are you trying to autorelease the object in the first place? If not try this
ReportDetailItem *projectContainer;
if (![areaContainer.items objectForKey:projectKey]) {
projectContainer = [[ReportDetailItem alloc] init];
} else {
projectContainer = [[areaContainer.items objectForKey:projectKey] retain];
}
projectContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [projectContainer.total intValue])];
[areaContainer.items setObject:projectContainer forKey:projectKey];
if(projectContainer) {
[projectContainer release];
projectContainer = nil;
}