Guillaume
Guillaume

Reputation: 9061

Memory leaks with alloc in Objective-C

I'm new in Objective-C and so I've some problem to understand the memory management. I create a project without ARC (to understand the memory management), and I would like to know how to avoid memory leaks.

I explain with some C++ code. For example, I want to create one matrix with 3 vectors so in C++ we can do (first method):

Matrix mat(Vector(1, 1, 1), Vector(2, 2, 2));

At the end of the function all objects are destroyed and there're no memory leaks

In Objective-C we do something like that :

Matrix mat = [[Matrix alloc] init:[[Vector alloc] init:1:1:1]:[[Vector alloc] init:2:2:2]];

Which in C++ is (second method):

Matrix mat = new Matrix(new Vector(1, 1, 1), new Vector(2, 2, 2));

But by this way, we can't destroy the 2 vectors and so we create memory leaks.

So my question is, is there a way to do the first method in Objective-C ? And if not, I think I have to create 2 temporary variables, which contain the 2 vectors and release after the initialization of the matrix or there's another method to do that ?

Upvotes: 0

Views: 589

Answers (4)

Gabriele Petronella
Gabriele Petronella

Reputation: 108101

Besides from the fact that your syntax is not really Obj-C compliant, there's several options for managing the memory in such a case.

NOTE: I'm assuming that you don't want to store the parameters into variables, otherwise it would just a matter of releasing such variables after passing them to the constructor. With ARC this is not a big difference, since you don't need to release in any case.

No ARC. Factory methods

Factory methods in Objective-C are autoreleased by convention. This convention is so strong that even ARC will expect such methods to be autoreleased, so beware

Matrix *mat = [Matrix matrixWithVectors:[Vector vectorWithCoordinatesX:1 y:1 z:1],
                                        [Vector vectorWithCoordinatesX:1 y:1 z:1],
                                        [Vector vectorWithCoordinatesX:1 y:1 z:1], nil];

The implementation of vectorWithCoordinates, as an example, should be something like

+ (Vector *)vectorWithCoordinatesX:(CGFloat)x y:(CGFloat)y z:(CGFloat)z {
    return [[[Vector alloc] initWithCoordinatesX:x y:y z:z] autorelease];
}

No ARC. Autorelease

If you are not holding reference to your parameters since you're creating them inline, you can also explicitly autorelease them.

Matrix *mat = [[[Matrix alloc] initWithVectors:[[[Vector alloc] initWithCoordinatesX:1 y:1 z:1] autorelease],
                                               [[[Vector alloc] initWithCoordinatesX:1 y:1 z:1] autorelease],
                                               [[[Vector alloc] initWithCoordinatesX:1 y:1 z:1] autorelease], nil] autorelease];

ARC

In ARC there's no need for releasing, since the compiler will take care of it.

So both

Matrix *mat = [Matrix matrixWithVectors:[Vector vectorWithCoordinatesX:1 y:1 z:1],
                                        [Vector vectorWithCoordinatesX:1 y:1 z:1],
                                        [Vector vectorWithCoordinatesX:1 y:1 z:1], nil];

and

Matrix *mat = [[Matrix alloc] initWithVectors:[[Vector alloc] initWithCoordinatesX:1 y:1 z:1],
                                              [[Vector alloc] initWithCoordinatesX:1 y:1 z:1],
                                              [[Vector alloc] initWithCoordinatesX:1 y:1 z:1], nil];

are correct.

My assumption here is also that the Matrix constructors take an arbitrary number of vectors as arguments, nil terminated.

Upvotes: 2

mmmmmm
mmmmmm

Reputation: 32661

You need to release all objects you allocate like C++ new and delete. So one way is as you say create temporary variables and then release them.

However there is also autorelease which will release the objects when the autorelease pool containing them is released, e.g. in the main NSApplication loop if you have not created your own pool. This allows you in this case to do the memory management in line.

So

Matrix mat = [[[Matrix alloc] init:[[[Vector alloc] init:1:1:1]autorelease]
                  :[[[Vector alloc] init:2:2:2]autorelease]
                  autorelease];

See Apple's docs on memory management as a start.

I would look at a few examples to learn the mechanism but go to ARC to do any real work - you might need to understand the underlying release mechanism to debug.

Upvotes: 0

Kurt Revis
Kurt Revis

Reputation: 27984

You're correct that you would need to use temporary variables to correctly release the objects.

Normally in pre-ARC code, you'd use autorelease instead:

Matrix mat = [[Matrix alloc] init:[[[Vector alloc] init:1:1:1] autorelease] :[[[Vector alloc] init:2:2:2] autorelease]];

Or, even better, you'd add a convenience method to Vector:

+ (Vector*) vector:(int)a :(int)b :(int)c
{
    return [[[Vector alloc] init:a :b :c] autorelease];
}

then use it:

Matrix mat = [[Matrix alloc] init:[Vector vector:1 :1 :1] :[Vector vector:2 :2 :2]];

(By the way, it's bad form to name your methods without words before the colons -- it's difficult to read.)

Upvotes: 1

dasdom
dasdom

Reputation: 14063

You Objective-C code is wrong. You are right with your assumption

So my question is, is there a way to do the first method in Objective-C ? And if not, I think I have to create 2 temporary variables, which contain the 2 vectors and release after the initialization of the matrix or there's another method to do that ?

The alternative could be to use class methods to get autoreleased objects. The methods start with a plus sign. Or you could also autorelease the Vectors by your self.

[[[Vector alloc] init:1:1:1] autorelease];

By the way: In Objective-C you shouldn't use unnamed parameters. So your init should look like:

[[[Vector alloc] initWithX:1 y:1 z:1] autorelease];

But I strongly recommend to use ARC and stop worrying about memory management.

Upvotes: 0

Related Questions