views:

205

answers:

2

I have the following designated initializer:

-(id) initWithName:(NSString*)name;

and the following default initializer:

-(id) init { return [self initWithName:@"foo"]; }

What kind of object does the designated initializer receive than? A released or a autoreleased?

given the following initializer:

-(id) init { return [self initWithName:[NSString new]]; }

I would receive a retined object. The default initializer would never have a chance to release it, so I shouldn't retain it?. Now imagine instead of NSString this would be a class that does not provide a convenience initializer (like -myClassWithParam:). Do I need to provide a convenience initializer to enable in place constructing?

A: 

You should always send an autoreleased object (or release it after) to your initialisers - the memory management rules don't change in this instance.

In your example, I'd do this:

-(id) init {return [self initWithName:[[NSString new] autorelease]]]}

This will fix your memory leak and still allow in place constructing.

However, you don't need an extra retain when passing through your initializers - what you're doing with self is fine. As a rule, +alloc retains the object once, and -init methods don't retain further. -init will be called many times as the [super init] calls go up the class tree, but the object should only be retained once when it's finally returned to the caller - +alloc has already done this for you.

However, convenience methods that don't contain the words init, copy or new should return an autoreleased object. As an example:

+(MyObject *)objectWithName:(NSString *)aName {
    return [[[MyObject alloc] initWithName:aName] autorelease];
}

FWIW, I normally have -init as my designated initializer, so in case I forget and send -init to an object (or someone else does the same), you don't get a trash object. For instance:

-(id)init {
    if (self = [super init]) {
        [self setName:[[NSString new] autorelease]];

        myReallyImportantiVar = [[NSArray alloc] init];
        // etc;
    }
    return self;
}


-(id)initWithName:(NSString  *)aName {
    if (self = [self init]) {
        [self setName:aName];
    }
    return self;
}

This may be slightly less efficient (when you're using -initWithName:, setName is called twice), but it's much safer.

iKenndac
`[[NSString new] autorelease]` is an obfuscated way to say `[NSString string]` or just `@""`
Nikolai Ruhe
Johannes' code is not retaining `self` in `init`.
Nikolai Ruhe
I don't really like what you're doing with your initializers. The init method with the least arguments count should call the init method with one more arguments, where you pass a default variable (probably nil) for the extra parameter. I would get lost in the practice you're using, but it may be just a matter of style. The Cocoa way is to do it using the mechanism I described before. You can see a typical example in my Download class: http://github.com/JoostK/Download-Manager/blob/master/JKDownloadManager.m (line 61-119)
JoostK
@Nikolai Regarding retaining self, I was answering the part of the question where he asks if the designated initialiser should retain the object returned by init or not. I'm also aware of [NSString string] and @"", but I was trying to avoid string literals and convenience methods as that's what the OP is confused about. Perhaps I should've used a different class.
iKenndac
@iKenndac As I understand the question, Johannes is never talking about retaining the to-be-initialized object but only the string argument.
Nikolai Ruhe
+2  A: 

The initializer (designated or not) should never care about the ownership of objects it receives as arguments. If it wants to keep the object, it has to copy or retain it, no matter where the argument comes from. Your designated initializer initWithName should copy the name argument in its implementation.

Memory management for NSString literals is a special case, since these objects are never released and just ignore retain, release, and autorelease.

Your third example has a leak, since the name argument string object is never released.

Nikolai Ruhe
More generally, no method should care about the ownership of objects it receives. The only thing a method should rely on (unless it claims ownership) is that a parameter will survive for the duration of that method.
Chuck
Absolutely right. Thanks for this clarification.
Nikolai Ruhe
thanks for this clarifiaciton, I thought of this myself but wanted to have some other input. As you can guess, I am pretty new to obj-c
Johannes Rudolph