Memory leak after switching to #property declaration? - objective-c

Why Im having a memory leak here:
.h:
#property (nonatomic, retain) NSDictionary *info;
and then in my .m:
#synthesize info = _info;
and Im having one leak here:
- (id)initWithData:(NSDictionary *)data
{
self = [super init];
if (self) {
self.info = [[NSDictionary alloc] initWithDictionary:data];
}
return self;
}
And another leak here:
-(void)dealloc {
[self.info release];
[super dealloc];
}

Your leak stems from your property declaration and the way you assign it, when you declare a property as "retain" using the setter will retain the var for you, so doing
self.info = [[NSDictionary alloc] initWithDictionary:data];
Retains the var, and since alloc also retain the var you get a +2 retain count, you should either autorelease the object or assign it like
_info = [[NSDictionary alloc] initWithDictionary:data];
also when you release a property do so by setting it to nil
self.info=nil

Your constructor should use
self.info = [NSDictionary dictionaryWithDictionary:data];
Your dealloc should be simplified to
- (void)dealloc {
self.info = nil;
[super dealloc];
}

Related

Memory leak in objective-c class init method

I'm having problems with a leak in the init method of a class I have created. To keep it simple, I have the following (simplified) problem:
ViewController initialises an instance of
ClipData class which initialises an instance of
AnimationData class which initialise a string
ViewController:
myClipData = [[ClipData alloc] init];
ClipData:
- (id)init
{
self = [super init];
if (self) {
animData = [[AnimationData alloc] init]; //LEAK HERE
}
return self;
}
AnimationData:
- (id)init
{
self = [super init];
if (self) {
name = [NSString string];
}
return self;
}
All the objects in the classes are declared as (nonatomic, retain). I'm aware that doing this bumps up the retain count, but how do I initialise the AnimationData without leaking the animData???
Probably a very stupid question, so any help much appreciated.
Thanks,
Duncs
You are never releasing the animData. You need to add dealloc to your class:
- (void)dealloc {
[animData release];
[super dealloc];
}
Similarly, you need to add a similar dealloc to AnimationData.
On a related note, you need to retain and later release the string created in -[AnimationData init], what you are doing right now is essentially a noop, except that it leaves behind a garbled pointer.
When you have an alloc you must also have a release.
You should also reference the properties through self so you access the properties rather than the underlying members.
So you should really do :
ClipData *clip = [[ClipData alloc] init];
self.myClipData = clip;
[clip release];
And
if (self) {
AnimationData *data = [[AnimationData alloc] init];
self.animData = data;
[data release];
}
Make sure you also release the properties in the dealloc of the class by setting them to nil.
self.myClipData = nil;
self.animData = nil;

Why do I crash? Dealloc/release + property/retain weirdness

Code in my controller:
CalcBorderBlocks *calcBB = [[CalcBorderBlocks alloc] init];
NSMutableArray *blockArray = [calcBB calc:341241133];
[calcBB release]; // Releases calcBB.
Code in CalcBorderBlocks.h:
#import <Foundation/Foundation.h>
#interface CalcBorderBlocks : NSObject {
#private
NSMutableArray *blockArray_;
}
#property(nonatomic, retain) NSMutableArray *blockArray;
- (NSMutableArray *)calc:(int)blockID;
#end
Code in CalcBorderBlocks.m:
#import "CalcBorderBlocks.h"
#implementation CalcBorderBlocks
#synthesize blockArray = blockArray_;
- (id)init {
self = [super init];
if (self) {
blockArray_ = [[NSMutableArray alloc] init]; // Retain count should be 1
}
return self;
}
- (NSMutableArray *)calc:(int)blockID {
// Do stuff
return self.blockArray;
}
- (void)dealloc {
[blockArray_ release]; // Normal cleanup, yet crashes! Works fine if I comment out this line.
[super dealloc];
}
#end
If you're doing anything with blockArray after this line:
[calcBB release]; // Releases calcBB.
Then that's the cause of the crash.
You're releasing calcBB which in turn releases blockArray in dealloc.
I suspect you need to retain blockArray, process it as required then release it afterwards.
CalcBorderBlocks *calcBB = [[CalcBorderBlocks alloc] init];
NSMutableArray *blockArray = [[calcBB calc:341241133] retain];
[calcBB release]; // Releases calcBB.
// Process blockArray as needed
[blockArray release];
This:
CalcBorderBlocks *calcBB = [[CalcBorderBlocks alloc] init];
NSMutableArray *blockArray = [calcBB calc:341241133];
[calcBB release]; // Releases calcBB.
usually means that blockArray is retained but also autoreleased. If you want to keep it alive, you'll have to retain it yourself, otherwise it will be released by the autorelease pool after a while.
Post the backtrace of the crash. More likely than not, it is because you overreleased something in the array (or the array itself somewhere else.

Having problems with adding objects to NSMutableArray

I am having problem with adding objects to NSMutableArray *array.
// Controller.m
#import "Controller.h"
#implementation Controller
- (void)parser:(NSString *)string{
[array addObject:string];
NSLog(#"answerArray(1): %#",[array objectAtIndex:1]);
[array retain];
}
#end
// Controller.h
#import <Foundation/Foundation.h>
#interface Controller : NSObject {
NSMutableArray *array;
}
- (void)parser:(NSString *)string;
#end
NSLog(#"answerArray(1): %#",[array objectAtIndex:1]);
Results: answerArray(1): (null)
First off, you're over-retaining the array.
Second, you didn't provide the code for initializing the array, so I guess it's not allocated and initialized. This will cause the code to message a nil object and thus return nil.
You should create an init method for the Controller object, and allocate a new NSMutableArray object (and retain it).
Also, a proper dealloc to release the array.
NSMutabaleArray starts at index 0
Here is the method I added to Controller class:
- (id)init {
self = [super init];
if(self){
array = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
[array release];
[super dealloc];
}

Memory leak with objective-c on alloc

When I use Instruments to find memory leaks, a leak is detected on
Horaires *jour;
jour= [[Horaires alloc] init]; // memory leak reported here by Instruments
self.lundi = jour;
[jour release];
and I don't know why there is a leak at this point.
Does anyone can help me? Here's the code.
// HorairesCollection.h
#import <Foundation/Foundation.h>
#import "Horaires.h"
#interface HorairesCollection : NSObject < NSCopying > {
Horaires *lundi;
}
#property (nonatomic, retain) Horaires *lundi;
-init;
-(void)dealloc;
#end
// HorairesCollection.m
#import "HorairesCollection.h"
#implementation HorairesCollection
#synthesize lundi;
-(id)copyWithZone:(NSZone *)zone{
HorairesCollection *another = [[HorairesCollection alloc] init];
another.lundi = [lundi copyWithZone: zone];
[another autorelease];
return another;
}
-init{
self = [super init];
Horaires *jour;
jour= [[Horaires alloc] init]; // memory leak reported here by Instruments
self.lundi = jour;
[jour release];
return self;
}
- (void)dealloc {
[lundi release];
[super dealloc];
}
#end
// Horaires.h
#import <Foundation/Foundation.h>
#interface Horaires : NSObject <NSCopying>{
BOOL ferme;
BOOL h24;
NSString *h1;
}
#property (nonatomic, assign) BOOL ferme;
#property (nonatomic, assign) BOOL h24;
#property (nonatomic, retain) NSString *h1;
-init;
-(id)copyWithZone:(NSZone *)zone;
-(void)dealloc;
#end
// Horaires.m
#import "Horaires.h"
#implementation Horaires
-(BOOL) ferme {
return ferme;
}
-(void)setFerme:(BOOL)bFerme{
ferme = bFerme;
if (ferme) {
self.h1 = #"";
self.h24 = NO;
}
}
-(BOOL) h24 {
return h24;
}
-(void)setH24:(BOOL)bH24{
h24 = bH24;
if (h24) {
self.h1 = #"";
self.ferme = NO;
}
}
-(NSString *) h1 {
return h1;
}
-(void)setH1:(NSString *)horaire{
[horaire retain];
[h1 release];
h1 = horaire;
if (![h1 isEqualToString:#""]) {
self.h24 = NO;
self.ferme = NO;
}
}
-(id)copyWithZone:(NSZone *)zone{
Horaires *another = [[Horaires alloc] init];
another.ferme = self.ferme;
another.h24 = self.h24;
another.h1 = self.h1;
[another autorelease];
return another;
}
-init{
self = [super init];
return self;
}
-(void)dealloc {
[h1 release];
[super dealloc];
}
#end
You've set your property to retain and you alloc and release the variable, so from what I can see the code is okay and Instruments has given you a false warning.
I think your copyWithZone: might have a leak, though. [lundi copyWithZone:] will retain a copy of lundi but you never release it. So you need an extra release, something like this:
-(id)copyWithZone:(NSZone *)zone{
DefibHoraires *another = [[DefibHoraires alloc] init];
Horaires* makeCopy = [lundi copyWithZone: zone];
another.lundi = makeCopy;
[makeCopy release];
return another;
}
This is because copy and alloc both return retained object instances and you need to manually release them when you're finished with them. You did that correctly for your alloc'd objects but not the copy.
That init method looks ok, although it should be implemented (and typed) as
-(id)init
{
if (self = [super init])
{
...
}
return self;
}
or a similar pattern.
Your copyWithZone implementations are wrong, they need to return a retained object, so do not autorelease the returned value. But you need to release your copy of lundi, because you are using the retaining setter.
-(id)copyWithZone:(NSZone *)zone{
DefibHoraires *another = [[DefibHoraires alloc] init];
Horaires *lundiCopy = [lundi copyWithZone:zone];
another.lundi = lundiCopy;
[lundiCopy release];
return another;
}
I don't know why you return an instance of DefibHoraires here, shouldn't it be a HorairesCollection?
Maybe the wrong copyWithZone: method is responsible for the reported leak (it's a leak anyway).
One further note: It's a good defensive rule to use (copy) for NSString properties instead of (retain) to remove side effects when passing NSMutableString instead.
I don't have an answer but I do have some general comments:
In copyWithZone: you should use allocWithZone: (passing the same zone as a parameter) to allocate the object you are going to return.
copyWithZone: should return a retained object. Don't autorelease it.
You are not supposed to use properties in init. Your init should look something like:
-init
{
self = [super init];
if (self != nil)
{
lundi = [[Horaires alloc] init]; // assign the ivar directly
}
return self;
}
In your copyWithZone: for HorairesCollection you have a leak. It should look like:
-(id)copyWithZone:(NSZone *)zone{
DefibHoraires *another = [[DefibHoraires allocWithZone: zone] init];
another.lundi = [[lundi copyWithZone: zone] autorelease];
return another;
}

Why does this crash?

I made a class. This is the h file.
// MyClass.h
#import <Foundation/Foundation.h>
#interface MyClass : NSObject <NSCoding> {
NSString *string1;
NSString *string2;
}
#property (nonatomic, retain) NSString *string1;
#property (nonatomic, retain) NSString *string2;
#end
This is the m file.
// MyClass.m
#import "MyClass.h"
#implementation MyClass
#synthesize string1, string2;
- (void)encodeWithCoder:(NSCoder *)coder;
{
if (self = [super init]){
[coder encodeObject:string1 forKey:#"string1"];
[coder encodeObject:string2 forKey:#"string2"];
}
}
- (id)initWithCoder:(NSCoder *)coder;
{
self = [[MyClass alloc] init];
if (self != nil)
{
string1 = [coder decodeObjectForKey:#"string1"];
string2 = [coder decodeObjectForKey:#"string2"];
}
return self;
}
- (void)viewDidUnload {
self.string1 = nil;
self.string2 = nil;
}
- (void)dealloc {
[super dealloc];
[string1 release];
[string2 release];
}
#end
I created an array of these objects like this:
MyClass *object1 = [[MyClass alloc] init];
object1.string1 = #"object1 string1";
object1.string2 = #"string1 string2";
MyClass *object2 = [[MyClass alloc] init];
object2.string1 = #"object2 string1";
object2.string2 = #"object2 string2";
theArray = [[NSMutableArray alloc] initWithObjects:object1, object2, nil];
Then I saved the array like this:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:theArray] forKey:#"savedArray"];
Then I loaded the array from disk like this.
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"savedArray"];
if (dataRepresentingSavedArray != nil)
{
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (oldSavedArray != nil)
{
theArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
}
else {
theArray = [[NSMutableArray alloc] init];
}
}
The program crashes when it gets to this line in - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
cell.textLabel.text = [[theArray objectAtIndex:indexPath.row] string1];
Why does it crash there? It doesn't crash if I don't load the array from NSUserDefaults. However, I don't see anything I did wrong with saving or loading the array.
Edit:
I can also make it crash with this line of code:
NSLog(#"%#", [[theArray objectAtIndex:0] string1]) ;
In addition to cobbal's excellent points, your initWithCoder: method isn't using setters and therefore the strings aren't being retained. When you try to access string1 at your crash line, that string has probably already been released. Do this in initWithCoder: instead:
self.string1 = [coder decodeObjectForKey:#"string1"];
self.string2 = [coder decodeObjectForKey:#"string2"];
To address cobbal's first point, don't do any init at all in encodeWithCoder:. Just encode your object. Check out Apple's Archives and Serializations Programming Guide for Cocoa for more details about encoding and decoding objects.
A few things jump out at me
you're calling init in encodeWithCoder:. since you're not initializing anything here you should not be changing self
you're calling alloc in initWithCoder:. Once you're in an init method you shouldn't have to call alloc, just call self = [super init].
you have a viewDidUnload method. Even if this were called in an NSObject subclass, you probably don't want to get rid of your data when the view is unloaded.
you're calling [super dealloc] at the beginning of your dealloc method. It should go at the end.
I didn't read all of your code, so I am not sure if this is the problem, but in dealloc you should call [super dealoc] only after releasing the instance variables.
If it crashes due to being out of bounds, check that your UITableViewController correctly implements
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
and
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
to reflect the NSArray you're using as data source.