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!
1 Answer 1
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.