5

私はこれに対する答えを求めて Apple の委任とプロトコルのドキュメントを探し回っていましたが、1 日以上経った後、諦めて皆さんに試してもらうことにしました。HTTPManager、LoginManager、および FetchManager の 3 つのクラスがあります。これらのクラスが何をするかはおそらく推測できますが、明確にするために...

  • 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 *'

初期化される 2 つのクラスはどちらも 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;
}

...

前もって感謝します。

4

1 に答える 1

3

問題は、同じメソッド シグネチャ-initWithDelegate:を持つが、引数や戻り値の型が異なる 2 つのメソッドがあることです。コンパイラはこのケースをうまく処理できず、場合によっては実行時にエラーが発生する可能性もあります(メソッドの型のサイズに違いがなく、すべてポインターであるため、この場合ではありません)。

この理由(AFAIK)は、ランタイムがメソッドで使用される型に直接アクセスできないためです。セレクター (型情報を含まない) を読み取り、このセレクターに基づいて呼び出すメソッドを決定します。ランタイムがメソッド引数をスタックにパックできるようにするために、コンパイラはコンパイル時に、セレクターを引数と戻り値の型にマップするテーブルを作成します。このテーブルには、セレクタごとに 1 つのエントリしかありません。したがって、同じセレクターを持ち、引数または戻り値の型が異なる 2 つのメソッドが存在する場合、このシステムは失敗する可能性があります。

あなたの場合:

-init...メソッドはid、特定の型ではなく、常に返す必要があります。

これにより、さまざまな戻り値の型の問題が解決されます。もう 1 つの問題 (引数の型が異なる) は、解決が困難です。メソッド宣言からプロトコル仕様を省略するか ( initWithDelegate:(id)delegate)、2 つのメソッドに異なる名前を付けることができます。

- (id) initWithHttpMgrDelegate:(id <HTTPManagerDelegate>)delegate;
- (id) initWithLoginMgrDelegate:(id <LoginManagerDelegate>)delegate;
于 2010-12-08T19:38:53.983 に答える