views:

419

answers:

3

I'm working on learning Objective-C, and I'm trying to get a feel for the memory management. I'm fairly familiar with C memory management, but I'm trying to figure out how different ObjC is.

Let's say I have a class called Complex that is used for holding complex numbers, which has a method -(Complex*)add:(Complex*)c; that adds the passed in complex number to self (Complex is a mutable object, let's say).

So I can call it this way:

Complex *c = [[Complex alloc] withReal: -3.0 andImag: -2.4]; // c = -3.0-2.4i
[c add : [[Complex alloc] withReal: 1.0 andImag: 2.0]]; // now c = -2.0-0.4i

What happens to the memory used for the temporary object created in the call to add? I assume it's a memory leak; is this the correct code?

Complex *c = [[Complex alloc] withReal: -3.0 andImag: -2.4]; // c = -3.0-2.4i
Complex *b = [[Complex alloc] withReal: 1.0 andImag: 2.0]; // b = 1+2i
[c add : b]; // now c = -2.0-0.4i
[b release];

Bonus noob question: would the Objective-C 2.0 GC deal with the situation?

+2  A: 

In the first example:

[c add : [[Complex alloc] withReal: 1.0 andImag: 2.0]];

You have allocated object with reference count 1 and it is not released so it is memory leak because you don't store the pointer to that object so you can't send release to it.

You could modify it to autorelease the 'temporary' object:

[c add : [[[Complex alloc] withReal: 1.0 andImag: 2.0] autorelease]];

You would probably use init in the name to point out the fact that the object is not autoreleased and the user has to handle the memory management:

initWithReal:(double)r andImag:(double)i

The second example is correct.

Cocoa provides many methods (both static and member) that return autoreleased objects for example [NSString stringWithString:@"example"] so you could create methods that return autoreleased objects so you don't have to deal with releasing:

Complex * c = [Complex complexWithReal:1.0 andImag:2.0]

You could also create method that takes raw data e.g. something like addReal:(double)r andImag:(double)i so you can skip the alloc/init step.

Another option is to use structures, as used in Cocoa (CGRect and so on). They are the same as in C meaning they are allocated on stack and they disappear once they go out of scope, as opposed to allocated objects on heap with alloc/init. Good for 'small' objects that are frequently used but often you don't want to keep them around.

As to GC I don't see any reason why GC would not be able to handle releasing the object - I haven't used it much though (only looked at couple of examples - I prefer to manage memory myself - unless using python ...)

stefanB
+1  A: 

stefanB is correct. since you will be doing alot of this 'on the fly' instantiation (i would assume...) it would make sense to build a static 'convenience' constructor (there are tons of examples of these in the appKit & co.)

+(Complex*) complexWithReal:(double)r andImag:(double)i
{
  return [[Complex alloc] initWithReal:r andImag:i] autorelease];
}

which would make your call to add message then look like this:

[c add : [Complex complexWithReal:2.0 andImage:1.0]]

have fun!

kent
+2  A: 

First, with my "best practices" hat on, when you create custom classes, the Objective-C idiom is to name the initializers (equivalent of "constructors" in other languages) with a leading "init". Also, try not to abbreviate parts of the method selector, or the named parameters.

For example, instead of this:

- (id) withReal:(double)r andImag:(double)i { ... }

You should consider doing this:

- (id) initWithReal:(double)real imaginary:(double)imaginary { ... }

The "and" before the second part is a matter of taste — it's perfectly acceptable to use it, but I prefer it without, since you probably wouldn't use it for methods with 3+ parameters. For example, it would more verbose than necessary if you were creating an RGB color to use -initWithRed:andGreen:andBlue: — writing/using -initWithRed:green:blue: is usually preferable.


For your actual question, I completely agree that creating a convenience class constructor is the smart way to go for situations such as these. It's visually much cleaner than alloc-init-autorelease each time you need a throwaway instance. Matching the changes above, I would tweak @kent's answer to look like this:

+ (Complex*) complexWithReal:(double)real imaginary:(double)imaginary {
    return [[Complex alloc] initWithReal:real imaginary: imaginary] autorelease];
}


For the bonus question, yes, Objective-C 2.0's GC would handle this just fine; most well-written retain-release code works as-is under GC, especially if you are careful to set pointers to nil when you no longer need them. When GC is enabled, calls to retain/release/autorelease are essentially no-ops, and objects are collected once there are no references to them. Once the last pointer to an object is lost, or goes out of local scope, the object is eligible for collection. (Technically, the GC only counts strong references, but unless you're knowingly using weak references, the distinction is probably irrelevant for now.) Just remember that garbage collection is currently NOT supported on iPhone.

Quinn Taylor
Thanks for the GC tips - but it'll be a while before I have the scratch to start playing with an iPhone ;)
slide_rule
Sure thing. GC is great if you can use it, although it means you have to drop Tiger support, and all the frameworks you use have to support GC as well. But as time goes on, that will become more common, and who knows, perhaps as iPhones get faster, they'll start to support GC as well. :-)
Quinn Taylor