【问题标题】:Odd or erroneous warning with multiple @protocols带有多个 @protocols 的奇怪或错误警告
【发布时间】:2011-06-22 03:45:55
【问题描述】:

我遇到了一个奇怪的情况,我有两个协议和几个类,它们由实现协议的委托初始化。标准的东西,真的。但是,XCode 4 抛出了我似乎无法理解的错误警告。这是显示定义和实现的折叠源列表。

// CLASS A
@protocol ClassADelegate <NSObject>
-(void) DoSomething;
@end

@interface ClassA : NSObject {  }
+(ClassA *) classWithDelegate:(id<ClassADelegate>)d;
-(ClassA *) initWithDelegate:(id<ClassADelegate>)d;
@end

@implementation ClassA
+(ClassA *) classWithDelegate:(id<ClassADelegate>)d {
     return [[[ClassA alloc]initWithDelegate:d]autorelease];
}
-(ClassA *) initWithDelegate:(id<ClassADelegate>)d{
     self = [super init];
     return self;
}
@end

// CLASS B
@protocol ClassBDelegate <NSObject>
-(void) DoSomethingElse;
@end

@interface ClassB : NSObject<ClassADelegate> {  }
+(ClassB *) classWithDelegate:(id<ClassBDelegate>)d;
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d;
-(void) DoSomething;
@end

@implementation ClassB    
+(ClassB *) classWithDelegate:(id<ClassBDelegate>)d {
    return [[[ClassB alloc]initWithDelegate:d]autorelease];
}
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d{
    self = [super init];
    return self;
}
-(void) DoSomething {

}
@end

现在这是疯狂的部分——在这一行的 ClassB 的静态构造函数中:

return [[[ClassB alloc]initWithDelegate:d]autorelease];

...编译器正在抛出一个没有意义的警告:

"Type 'id <ClassBDelegate>' does not conform to the 'ClassADelegate' protocol"

我错过了什么吗?选择器清楚地将类型设置为id&lt;ClassBDelegate&gt;,所以d 应该是正确的。这只是编译器感到困惑吗?

【问题讨论】:

    标签: objective-c xcode4


    【解决方案1】:

    编译器会感到困惑,因为您的方法具有相同的名称但返回/参数类型不同,而 Objective-C 与方法重载并不完全一致。

    编译器分析时:

    [[[ClassB alloc]initWithDelegate:d]autorelease];
    

    开头是:

    [ClassB alloc]
    

    +[ClassB alloc]的返回类型是id,所以上面的表达式是id类型。下一步是分析:

    [##expression of type id## initWithDelegate:d]
    

    此时有两种可能的方法:

    -(ClassA *) initWithDelegate:(id<ClassADelegate>)d;
    -(ClassB *) initWithDelegate:(id<ClassBDelegate>)d;
    

    两者都是可能的,因为接收器的类型是id,所以它可以是ClassA 实例或ClassB 实例。但是,啊哈!,第一个参数的协议不同,所以不应该有任何混淆。但是,方法查找基于两个变量:是类方法还是实例方法,以及相应的选择器。在上面的方法中,两者都是实例方法,并且都具有相同的选择器。编译器决定选择第一个。

    一种解决方法是告诉编译器[ClassB alloc] 返回的对象是ClassB * 类型而不是泛型类型id

    return [[(ClassB *)[ClassB alloc]initWithDelegate:d]autorelease];
    

    并且可能在所有类似的类中使用这种模式。

    另一个解决方法是为这些方法赋予不同的名称,例如-initWithClassADelegate:-initWithClassBDelegate:

    【讨论】:

    • 是的。另请参阅-Wstrict-selector-match option
    • 已确认;从参数类型中删除&lt;ClassADelegate&gt;-[ClassA initWithDelegate:] 会导致警告消失。解释得很好!
    • 谢谢!我想我感到困惑的地方是我假设 [ClassB alloc] 返回了 ClassB 类型的实例而不是 id。
    猜你喜欢
    • 2013-08-08
    • 2012-01-27
    • 2014-08-20
    • 2012-03-04
    • 2011-12-08
    • 1970-01-01
    • 1970-01-01
    • 2014-05-05
    • 1970-01-01
    相关资源
    最近更新 更多