级联委托和“不按其说法行事的代码”

发布于 2024-10-06 12:49:01 字数 2125 浏览 0 评论 0原文

我一直在苹果的授权和协议文档中寻找这个问题的答案,但一天多后我决定放弃并让你们尝试一下。我有三个类: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 *'

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

_loginMgr = [[LoginManager alloc] initWithDelegate:self];

那么到底是什么让 LoginManager 的 initWithDelegate: 方法返回 HTTPManager* 呢?没有继承,我的返回类型是正确的,所以对我来说,这是一些我无法最好的黑暗形式巫毒。

这是我的应用程序的外壳。可能存在拼写错误和小的不一致之处,因此在假设语法问题之前请先询问我:

// 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;
}

...

提前致谢。

I've been searching around Apple's delegation and protocol documentation for an answer to this, but after more than a day I've decided to give up and let you guys have a shot at it. I have three classes: HTTPManager, LoginManager, and FetchManager. You can probably guess what these classes do, but to be explicit...

  • HTTPManager - Wraps NSURLConnection and provides a simple interface for LoginManager and FetchManager to do HTTP requests with authentication.
  • LoginManager / FetchManager - Basically the same class, but they respond to HTTPManager's messages differently.

HTTPManager expects a delegate to implement the HTTPManagerDelegate protocol and both LoginManager and FetchManager do this. The Login- and FetchManager classes also provide a protocol for my application delegate so that the data can make its way all the way back to the user interface.

Within my application delegate's init: method, I initialize both a login and a fetch manager and get the following warnings for both:

warning: class 'MyAppDelegate' does not implement the 'HTTPManagerDelegate' protocol
warning: incompatible Objective-C types assigning 'struct HTTPManager *', expected 'struct LoginManager *'

Neither of the two classes being initialized are derived from HTTPManager, but they do implement the HTTPManagerDelegate protocol. The line of code that produces the above warning is:

_loginMgr = [[LoginManager alloc] initWithDelegate:self];

So what on earth is making LoginManager's initWithDelegate: method return an HTTPManager*? There is no inheritance and my return types are correct, so to me this is some dark form voodoo that I cannot best.

Here is the shell of my application. There are probably typos and small inconsistencies so ask me before assuming a syntactical problem:

// 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;
}

...

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

私藏温柔 2024-10-13 12:49:01

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

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

在您的情况下:

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

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

- (id) initWithHttpMgrDelegate:(id <HTTPManagerDelegate>)delegate;
- (id) initWithLoginMgrDelegate:(id <LoginManagerDelegate>)delegate;

The problem is that you have two methods with the same method signature -initWithDelegate: but with different types in their arguments and/or return types. The compiler cannot handle this case very well and in certain cases, it could also lead to errors at runtime (not in your case because the types in your methods do not differ in size, they're all pointers).

The reason for this (AFAIK) is that the runtime has no straightforward access to the types used in a method. It just reads a selector (which contains no type information) and decides based on this selector what method to call. To help the runtime pack the method arguments onto the stack, the compiler creates a table at compile time that maps selectors to the argument and return value types. This table has just one entry per selector. So if two methods exist that have the same selector but different types in arguments or return value, this system can fail.

In your case:

-init... methods should always return id and not a specific type.

This solves the problem of different return types. The other problem (different argument types) is harder to solve. You can either omit the protocol specification from your method declaration (initWithDelegate:(id)delegate) or give the two methods different names:

- (id) initWithHttpMgrDelegate:(id <HTTPManagerDelegate>)delegate;
- (id) initWithLoginMgrDelegate:(id <LoginManagerDelegate>)delegate;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文