views:

263

answers:

2

I have the following base/derived class setup in Objective-C:

@interface ASCIICodeBase : NSObject {
 @protected
  char code_[4];
}
- (Base *)initWithASCIICode:(const char *)code;
@end

@implementation ASCIICodeBase
- (ASCIICodeBase *)initWithCode:(const char *)code len:(size_t)len {
  if (len == 0 || len > 3) {
    return nil;
  }
  if (self = [super init]) {
    memset(code_, 0, 4);
    strncpy(code_, code, 3);
  }
  return self;
}
@end

@interface CountryCode : ASCIICodeBase
- (CountryCode *)initWithCode:(const char *)code;
@end
@implementation CountryCode
- (CountryCode *)initWithCode:(const char *)code {
  size_t len = strlen(code);
  if (len != 2) {
    return nil;
  }
  self = [super initWithCode:code len:len]; // here
  return self;
}
@end

On the line marked "here", I get the following gcc warning:

warning: incompatible Objective-C types assigning 'struct ASCIICodeBase *', expected 'struct CurrencyCode *'

Is there something wrong with this code or should I have the ASCIICodeBase return id? Or maybe use a cast on the "here" line?

+1  A: 

You should cast the return value to (CountryCode *). The method returns a value of type ASCIICodeBase *, which is less specific than CountryCode *

So:

self = (CountryCode *) [super initWithCode:code len:len];

But what you should really do is set the return type to (id). That's how it's usually done in Objective-C.

Philippe Leybaert
Seems correct to me, why is this down-voted?
bddckr
I have no idea. When you're participating on StackOverflow, I guess you have to get used to these coward-downvotes.
Philippe Leybaert
Yeah, why would this get downvoted? It's the reason the warning is appearing. People are strange.
Jason Coco
+2  A: 

Use (id) as the return value type.

Objective-C doesn't support covariant declarations. Consider:

@interface NSArray:NSObject
+ (id) array;
@end

Now, you can call +array on both NSArray and NSMutableArray. The former returns an immutable array and the latter a mutable array. Because of Objective-C's lack of covariant declaration support, if the above were declared as returning (NSArray*), clients of the subclasses method would have to cast to `(NSMutableArray*). Ugly, fragile, and error prone. Thus, using the generic type is, generally, the most straightforward solution.

So... if you are declaring a method that returns an instance of a specific class, typecast explicitly. If you are declaring a method that will be overridden and that override may return a subclass and the fact that it returns a subclass will be exposed to clients, then use (id).

Designated initializers work the same way. An -init* method may return an instance of just about any type, depending on context of implementation (of course). Thus, the return type of initialization methods is covariant and, as a result, you need to use (id) as the return type.

No need to file a bug -- there are several already.

bbum
I was expecting the covariant return type support I'm used to in C++. (id) for such base class messages it is.
gavinbeatty

related questions