Reputation: 53
This code works:
monkey.h
@interface monkey : NSObject {
NSNumber *monkeyRanch;
}
@property (nonatomic, retain) NSNumber *monkeyRanch;
-(id) gatherTheMonkeys:(int)howmany;
monkey.m
@synthesize monkeyRanch;
-(id) gatherTheMonkeys:(int)howmany{
NSNumber *temp=[[NSNumber alloc] initWithInt:howmany];
monkeyRanch = temp;
[temp release];
return [monkeyRanch autorelease];
}
appDelegate.m
theMonkeys = [[monkey alloc] gatherTheMonkeys:3];
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
..and theMonkeys
are released in dealloc. There are no leaks in Instrument.
But, if I try the same with an NSMutable
array instead of the NSNumber
:
monkey.h
@interface monkey : NSObject {
NSMutableArray *monkeyRanch;
}
@property (nonatomic, retain) NSMutableArray *monkeyRanch;
-(id) initTheMonkeys:(int)howmany;
monkey.m @synthesize monkeyRanch;
-(id) initTheMonkeys:(int)howmany{
monkeyRanch=[[NSMutableArray alloc] init];
NSNumber *imp =[[NSNumber alloc] initWithInteger:howmany];
[monkeyRanch addObject:imp];
[imp release];
return [monkeyRanch autorelease];
}
appDelegate
theMonkeys = [[monkey alloc] initTheMonkeys:3];
[theMonkeys retain];
this causes a leak (of an object 'monkey') just at the start of the application.
I tried to change the initTheMonkeys
to the following:
NSMutableArray *temp=[[NSMutableArray alloc] init];
monkeyRanch=temp;
[temp release];
NSNumber *imp =[[NSNumber alloc] initWithInteger:howmany];
[monkeyRanch addObject:imp];
[imp release];
return [monkeyRanch autorelease];
but the retain count of monkeyRanch
got to zero as releasing temp and the app crashed happily.
What am I doing wrong, and how can I fix it?
Upvotes: 1
Views: 814
Reputation: 119106
Your memory problems boil down to an issue of ownership. When a class has an instance variable (such as monkeyRanch
), you typically create an instance in the initializer for that class, and then release it in dealloc
, like so:
@interface Monkey : NSObject {
NSMutableArray * monkeyRanch;
}
@end
@implementation Monkey
- (id)init {
if ((self = [super init]) == nil) { return nil; }
monkeyRanch = [[NSMutableArray alloc] initWithCapacity:0];
return self;
}
- (void)dealloc {
[monkeyRanch release];
}
@end
In this case, the Monkey
class owns the monkeyRanch
member.
Within function calls, you must be sure to release
any local objects that you init
, retain
or copy
. It looks like you have this part correct, but I'll show a quick example all the same:
- (void)someFunction {
NSNumber * myNumber = [[NSNumber alloc] init];
...
[myNumber release];
}
The next thing to understand is that there is a huge difference between simply setting a member variable, and setting a property value. That is to say:
NSMutableArray * temp = [[NSMutableArray alloc] initWithCapacity:0];
monkeyRanch = temp;
[temp release]; // it's gone!
is not the same thing as:
NSMutableArray * temp = [[NSMutableArray alloc] initWithCapacity:0];
self.monkeyRanch = temp; // now owned by self
[temp release];
In the first case, you are simply setting a pointer (monkeyRanch) equal to another pointer (temp). The value is not retained, or copied.
In the second case, you are actually setting the property. This means that, assuming the property was set to retain, the array is now owned by your class object.
P.S.:
You may also be going down the wrong track design-wise. To my mind, a monkey ranch is something that has a lot of monkeys in it. If that's true, then your Monkey
objects really shouldn't own the MonkeyRanch. I would instead go with something like this:
Monkey.h
@class MonkeyRanch;
@interface Monkey : NSObject {
MonkeyRanch * ranch;
}
@property (nonatomic, assign) MonkeyRanch * ranch;
@end
Monkey.m
@implementation Monkey
@synthesize ranch;
@end
MonkeyRanch.h
#import "Monkey.h"
@interface MonkeyRanch : NSObject {
NSMutableArray * monkeys;
}
@property (nonatomic, retain) NSMutableArray * monkeys;
- (id)initWithNumberOfMonkeys:(int)howMany;
- (void)dealloc;
@end
MonkeyRanch.m
@implementation MonkeyRanch
@synthesize monkeys;
- (id)initWithNumberOfMonkeys:(int)howMany {
if ((self = [super init]) == nil) { return nil; }
monkeys = [[NSMutableArray alloc] initWithCapacity:howMany];
for (int index = 0; index < howMany; index++) {
Monkey * newMonkey = [[Monkey alloc] init];
[newMonkey setRanch:self];
[monkeys addObject:newMonkey];
[newMonkey release];
}
return self;
}
- (void)dealloc {
[monkeys release];
}
@end
Usage:
#import "MonkeyRanch.h"
MonkeyRanch * theRanch = [[MonkeyRanch alloc] initWithNumberOfMonkeys:3];
...
[theRanch release];
Upvotes: 3
Reputation: 24040
Indeed, you're missing this:
theMonkeys = [[monkey alloc] initTheMonkeys:3];
[theMonkeys retain];
When you 'init' something, you end up with an implicit 'retain' (the retain count is one). There's your leak; you're both init'ing it and retain'ing it.
Upvotes: 1