级联代表和"不做它说的代码"

The*_*iac 5 xcode cocoa delegation objective-c

我一直在寻找Apple的代表团和协议文档来寻找答案,但是经过一天多的努力,我决定放弃,让你们有机会接受它.我有三个类:HTTPManager,LoginManager和FetchManager.你可能猜到这些类的作用,但要明确......

  • HTTPManager - 包装NSURLConnection并为LoginManager和FetchManager提供一个简单的接口,用于通过身份验证执行HTTP请求.
  • LoginManager/FetchManager - 基本上是同一个类,但它们以不同的方式响应HTTPManager的消息.

HTTPManager期望委托实现HTTPManagerDelegate协议,LoginManager和FetchManager都执行此操作.Login-和FetchManager类还为我的应用程序委托提供协议,以便数据可以一直回到用户界面.

在我的应用程序委托init:方法中,我初始化了登录和获取管理器,并为两者获取以下警告:

warning: class 'MyAppDelegate' does not implement the 'HTTPManagerDelegate' protocol
warning: incompatible Objective-C types assigning 'struct HTTPManager *', expected 'struct LoginManager *'
Run Code Online (Sandbox Code Playgroud)

初始化的两个类都不是从HTTPManager派生的,但它们确实实现了HTTPManagerDelegate协议.产生上述警告的代码行是:

_loginMgr = [[LoginManager alloc] initWithDelegate:self];
Run Code Online (Sandbox Code Playgroud)

那么究竟是什么让LoginManager的initWithDelegate:方法返回HTTPManager*?没有继承,我的返回类型是正确的,所以对我来说这是一些我不能最好的黑暗巫术.

这是我的应用程序的shell.可能存在拼写错误和小的不一致,所以在假设语法问题之前请问我:

// HTTPManager.h

@protocol HTTPManagerDelegate
...
@end

@interface HTTPManager : NSObject
{
    id <HTTPManagerDelegate> _delegate;
    ...
}

- (HTTPManager *) initWithDelegate:(id <HTTPManagerDelegate>)delegate;
...

@end

// LoginManager.h

@protocol LoginManagerDelegate
...
@end

@interface LoginManager : NSObject <HTTPManagerDelegate>
{
    id <LoginManagerDelegate> _delegate;
    ...
}

- (LoginManager *) initWithDelegate:(id <LoginManagerDelegate>)delegate;
...

@end

// MyAppDelegate.h

@interface MyAppDelegate : NSObject <NSApplicationDelegate, LoginManagerDelegate, FetchManagerDelegate>
{
    LoginManager *_loginMgr;
    ...
}

...

@end

// MyAppDelegate.m

...

- (MyAppDelegate *) init
{
    self = [super init];

    if (self)
    {
        // WARNING HAPPENS HERE
        _loginMgr = [[LoginManager alloc] initWithDelegate:self];
        ...
    }

    return self;
}

...
Run Code Online (Sandbox Code Playgroud)

提前致谢.

Ole*_*ann 3

问题是您有两个方法具有相同的方法签名-initWithDelegate:,但其参数和/或返回类型具有不同的类型。编译器无法很好地处理这种情况,在某些情况下,它还可能导致运行时错误(不是在您的情况下,因为方法中的类型大小没有差异,它们都是指针)。

其原因(据我所知)是运行时无法直接访问方法中使用的类型。它只是读取一个选择器(不包含类型信息)并根据该选择器决定调用什么方法。为了帮助运行时将方法参数打包到堆栈上,编译器在编译时创建一个表,将选择器映射到参数和返回值类型。该表每个选择器只有一个条目。因此,如果存在两个具有相同选择器但参数或返回值类型不同的方法,则该系统可能会失败。

在你的情况下:

-init...方法应该始终返回id而不是特定类型。

这样就解决了返回类型不同的问题。另一个问题(不同的参数类型)更难解决。您可以在方法声明 ( initWithDelegate:(id)delegate) 中省略协议规范,也可以为这两个方法指定不同的名称:

- (id) initWithHttpMgrDelegate:(id <HTTPManagerDelegate>)delegate;
- (id) initWithLoginMgrDelegate:(id <LoginManagerDelegate>)delegate;
Run Code Online (Sandbox Code Playgroud)

  • 实际上,编译器可以从接收者的类型推断出正确的方法签名 - *如果*接收者是静态类型的,也就是说。这里的问题是“alloc”返回“id”,因此编译器没有可使用的信息。 (2认同)