Почему gcc предупреждает о несовместимом назначении структур с помощью `self = [super initDesignatedInit]; позвонить в производный класс? - PullRequest
3 голосов
/ 12 марта 2010

У меня есть следующие настройки базового / производного класса в 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

В строке, помеченной здесь, я получаю следующее предупреждение gcc:

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

Что-то не так с этим кодом, или я должен получить ASCIICodeBase return id? Или, может быть, использовать приведение в строке "здесь"?

Ответы [ 2 ]

4 голосов
/ 13 марта 2010

Используйте (id) в качестве типа возвращаемого значения.

Objective-C не поддерживает ковариантные объявления. Рассмотрим:

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

Теперь вы можете звонить +array на NSArray и NSMutableArray. Первый возвращает неизменный массив, а второй - изменяемый массив. Из-за отсутствия поддержки в Objective-C ковариантного объявления, если бы вышеупомянутое было объявлено как возвращающее (NSArray*), клиенты метода подклассов должны были бы привести к `(NSMutableArray *). Гадкий, хрупкий и подверженный ошибкам. Таким образом, использование универсального типа, как правило, является наиболее простым решением.

Итак ... если вы объявляете метод, который возвращает экземпляр определенного класса, типизируйте явно. Если вы объявляете метод, который будет переопределен, и это переопределение может вернуть подкласс и тот факт, что он возвращает подкласс, будет выставлено клиентам, тогда используйте (id).

Назначенные инициализаторы работают так же. Метод -init* может возвращать экземпляр практически любого типа, в зависимости от контекста реализации (конечно). Таким образом, возвращаемый тип методов инициализации является ковариантным, и в результате вам необходимо использовать (id) в качестве возвращаемого типа.

Не нужно регистрировать ошибку - их уже несколько.

<Ч />

Обратите внимание, что теперь в LLVM есть ключевое слово instancetype, которое можно использовать вместо id в объявлении, подобном приведенному выше. Это означает, что «этот метод возвращает экземпляр, который успешно проходит isKindOfClass: тест класса, для которого он был вызван».

4 голосов
/ 12 марта 2010

Вы должны привести возвращаемое значение к (CountryCode *). Метод возвращает значение типа ASCIICodeBase *, которое менее специфично, чем CountryCode *

Итак:

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

Но что вам действительно нужно сделать, так это установить тип возвращаемого значения (id). Вот как это обычно делается в Objective-C.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...