3
\$\begingroup\$

How could this constructor code be improved? I feel like I'm not doing something optimally here...

@interface JGProduct : NSObject {
    NSString *identifier;
}

+(JGProduct *)productWithIdentifier:(NSString *)identifier_;
-(JGProduct *)initWithIdentifier:(NSString *)identifier_;

@end

@implementation JGProduct

+(JGProduct *)productWithIdentifier:(NSString *)identifier_ {
    return [[[[self class] alloc] initWithIdentifier:identifier_] autorelease];
}

-(JGProduct *)initWithIdentifier:(NSString *)identifier_ {
    self = [super init];
    if (self) {
        _identifier = [identifier_ retain];
    }

    return self;
}

@end

id or JGProduct return type?

I see a lot of people writing

-(id)initWithIdentifier:(NSString *)identifier_ {

instead - why is this?

Surely any subclass of JGProduct would be exchangeable for the superclass... Or should I be returning something that is id but conforms to a protocol (if present)?

JGProduct or [self class] in initialiser?

I also see this:

+(JGProduct *)productWithIdentifier:(NSString *)identifier_ {
    return [[[JGProduct alloc] initWithIdentifier:identifier_] autorelease];
}

or

+(JGProduct *)productWithIdentifier:(NSString *)identifier_ {
    return [[[self alloc] initWithIdentifier:identifier_] autorelease];
}

Why would someone write either of these? The top one surely would not make the right kind of object if ran by a subclass. And what's the difference between self and [self class] if you're in a class method?

Thanks for any feedback!

\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

Why -init... should be declared to return id

Imagine you have class A and its subclass B sharing the same designated initializer, initWithString:.

@interface A : NSObject
-(A *) initWithString: (NSString *) string;
@end

@interface B : A
-(B *) initWithString: (NSString *) string;
@end

As you can see, the declarations have a conflicting return type, which unavoidably causes a compiler warning. You can trivially fix it by changing the methods to return id, which, given Objective-C dynamic typing, doesn't change anything about how these methods work. So it has become a well-established, but not as well-understood, tradition.

Why class initializers should use self rather than the class name

As you correctly noticed, using JGProduct instead of self will allocate an instance of JGProduct for any subclass, which is not what you want in 99% cases.

Why write +[JGProduct productWithIdentifier:]?

Most of the time, it's just a shortcut to alloc, init and autorelease to make calling code more readable. However, these initializers may be much more complicated and more efficient when implemented in class clusters. When you write a class cluster, you have one public superclass and several private subclasses. In such cases the superclass's alloc has to return a placeholder object which will allocate and instantiate an object of the right subclass in its init... implementation. The class initializer, on the other hand, can immediately return an already inited object of the right subclass.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.