为什么gcc警告不相容的struct赋值与' self = [super initdesignatinit];'调用派生类?

时间:2021-02-04 03:12:03

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

我在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:

在标有“此处”的一行中,我得到以下gcc警告:

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

警告:不兼容的Objective-C类型分配“struct ASCIICodeBase *”,预期为“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?

这段代码有什么问题吗?还是应该让ascii码返回id?或者在“这里”行上使用石膏?

2 个解决方案

#1


4  

Use (id) as the return value type.

使用(id)作为返回值类型。

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

Objective-C不支持协变声明。考虑:

@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.

现在,可以在NSArray和NSMutableArray上调用+array。前者返回一个不可变数组,后者返回一个可变数组。由于Objective-C缺少协变声明支持,如果上面声明为返回(NSArray*),那么子类方法的客户将不得不转换为' (NSMutableArray*)。丑陋、脆弱和容易出错。因此,一般来说,使用泛型类型是最直接的解决方案。

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).

所以…如果您正在声明一个方法,该方法返回一个特定类的实例,则显式地使用typecast。如果您声明要重写的方法,并且该重写可能返回一个子类,并且返回一个子类的事实将被公开给客户端,那么使用(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.

指定的初始化程序以相同的方式工作。一个-init*方法可以返回几乎任何类型的实例,这取决于实现的上下文(当然)。因此,初始化方法的返回类型是协变的,因此需要使用(id)作为返回类型。

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

不需要提交错误——已经有几个了。


Note that LLVM now has an instancetype keyword that can be used in place of id in a declaration like the above. It means "this method returns an instance that passes an isKindOfClass: test of the class upon which it was called", effectively.

注意,LLVM现在有一个instancetype关键字,可以在类似上面的声明中使用。它意味着“该方法返回通过isKindOfClass:它所调用的类的测试的实例”。

#2


4  

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

您应该将返回值转换为(CountryCode *)。该方法返回一个类型为ascii *的值,它比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.

但是,您真正应该做的是将返回类型设置为(id)。这就是Objective-C中通常的做法。

#1


4  

Use (id) as the return value type.

使用(id)作为返回值类型。

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

Objective-C不支持协变声明。考虑:

@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.

现在,可以在NSArray和NSMutableArray上调用+array。前者返回一个不可变数组,后者返回一个可变数组。由于Objective-C缺少协变声明支持,如果上面声明为返回(NSArray*),那么子类方法的客户将不得不转换为' (NSMutableArray*)。丑陋、脆弱和容易出错。因此,一般来说,使用泛型类型是最直接的解决方案。

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).

所以…如果您正在声明一个方法,该方法返回一个特定类的实例,则显式地使用typecast。如果您声明要重写的方法,并且该重写可能返回一个子类,并且返回一个子类的事实将被公开给客户端,那么使用(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.

指定的初始化程序以相同的方式工作。一个-init*方法可以返回几乎任何类型的实例,这取决于实现的上下文(当然)。因此,初始化方法的返回类型是协变的,因此需要使用(id)作为返回类型。

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

不需要提交错误——已经有几个了。


Note that LLVM now has an instancetype keyword that can be used in place of id in a declaration like the above. It means "this method returns an instance that passes an isKindOfClass: test of the class upon which it was called", effectively.

注意,LLVM现在有一个instancetype关键字,可以在类似上面的声明中使用。它意味着“该方法返回通过isKindOfClass:它所调用的类的测试的实例”。

#2


4  

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

您应该将返回值转换为(CountryCode *)。该方法返回一个类型为ascii *的值,它比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.

但是,您真正应该做的是将返回类型设置为(id)。这就是Objective-C中通常的做法。