why do I need this autorelease after [NSMutableArray array] to avoid a memory leak?
why do I need this autorelease after [NSMutableArray array] to avoid a memory leak?
That is Instruments told me there was a leak. By putting the autorelease in it solved it, however I'm not sure why this would be required. The "array" method wasn't like an INIT or COPY etc...
@interface Weekend : NSObject {
NSMutableArray* _events;
}
@property (nonatomic, retain) NSMutableArray* events;
@end
@implementation Weekend
@synthesize events = _events;
- (id)init {
if (self == [super init])
{
self.events = [[NSMutableArray array] autorelease]; // WHY IS THIS AUTORELEASE REQUIRED
}
return self;
}
- (void) dealloc {
[_events release]; _events = nil;
[super dealloc];
}
@end
NOTE: This is what I see in Intruments when I take autorelease out (and after I changed the "if (self == [super init])" to "if ((self = [super init]))"
# Category Event Code Location
0 __NSArrayM Malloc at the [NSMutableArray开发者_StackOverflow中文版 array] point
1 __NSArrayM Autorelease at the [NSMutableArray array] point
2 __NSArrayM Retain at the @synthesize events = _events; point of the code
3 __NSArrayM Release QuartzCore - CA:Transaction::observer_callback(__CF........)
(from main.m:14 - "int retVal = UIApplicationMain(argc, argv, nil, nil);")
Why do you need that extra release? You don't. Not there, anyway.
The problem is you're overretaining _events
somewhere else. Maybe you're passing it to another class that's retaining without releasing? Leaks are always attributed by Instruments to creation of the object, not the unbalanced retain.
Adding that autorelease instead of finding the unbalanced retain is the memory management equivalent of having an answer that's off by 0.3 and just adding 0.3 to fix it. You need to remove that and fix the real problem.
Edit: After reading your latest edit, I think you'll probably find that Weekend
itself is being leaked.
This line is wrong:
self.events = [[NSMutableArray array] autorelease];
You shouldn't be autoreleasing there. If didn't call alloc/init and used a class method that returns an object, by convention, those objects are return autoreleased.
You might think that autoreleasing it again will prevent a leak what you are doing is causing the object to dealloc too soon because of uneven releases. Chances are is that, while this seems like it's releasing the object, something else is probably retaining and will probably crash later.
Here is what is going on:
Autorelease is a pool that basically tells the system to queue up a release for later. Read up on NSAutoreleasePool. There is an NSAutoreleasePool at the top of the mainloop in the mainthread usually.
Something is else is that the array function will return an auto released version by convention.
The + (NSArray *)array
function looks something like this:
+ (NSArray *) array {
NSArray *ret = [[NSArray alloc] init];
return [ret autorelease];
}
Your property also has a "retain
" flag so the synthesized methods look something like this:
- (NSArray *)events
{
return [[_events retain] autorelease];
}
- (void)setEvents:(NSArray *)anEvents
{
if (_events != anEvents) {
[_events autorelease];
_events = [anEvents retain];
}
}
The setter here will retain the array that is passed in.
The problem is that you are autoreleasing an object was already just autoreleased, and then only retaining 1 more time. You have an embalance.
Basically retain count started off at 1 (as all objects do when they are alloc'd), was scheduled to autorelease down twice (which will subtract two retains when the pool is flushed at some point in the future) and it's retained once by the setter. By the end, retain count will zero and the object will be freed when you don't expect it.
basically:
[NSMutableArray array]
... array's retain count is 1, autoreleases queued to subtract 1[array autorelease]
... array's retain count is still 1, but now 2 autoreleases are queued so subtract 2[self setEvents:...]
... array is retained 1 more time so now count is 2, auto release queue still has it queued to subtract 2...- (sometime later when
[NSAutoreleasePool drain/release]
is called, usually by event loop pump).. array's is released twice because it was queued for autorelease previously twice... retain count equals 0. array is free'd.
Now something maybe retaining your array somewhere else which is why it's sticking around.
For start change if (self == [super init])
into if (self = [super init])
Then run static analyzer first, fix issues and then go Leak hunting with Instruments. Either way, I'm pretty sure leak will disappear when you fix if (self == ...) error.
Note: If you cannot reproduce leak in this way, try couple of times more. What happened for the first couple of times I ran this code is this (taken from The Obj-C Programming language):
You should assign self to the value returned by the initializer because the initializer could return an object different from the one returned by the original receiver.
In my case, self became nil after it got into if block, obviously... Does someone knows under which circumstances this might happen?
Clarification:
if (self == [super init]) // End result is YES, execution goes inside of the block but **==** DOES NOT ASSIGN [super init] object to self, single **=** DOES. See the difference? == vs =?
{
self.myArray = [NSMutableArray array]; // Allocates array on autorelease, assigns it to nil object trough property (code below) which increases retain count, even though self == nil;
}
return self; // self is nil, dealloc can't decrease retain count on _myArray because once you do self.myArray = [NSMutableArray array]; when self is nil, you get dangling pointer and no object has reference to instance of that array, memory leak being end result.
- (void)setMyArray:(NSMutableArray *)inArray
{
if (_myArray != inArray)
{
[_myArray release];
_myArray = [inArray retain];
}
}
If changing to if (self = [super init])
did not fix this leak, the cause lies somewhere else.
精彩评论