1. ホーム
  2. ios

[解決済み] Objective-Cでデリゲートを作成するにはどうしたらいいですか?

2022-03-23 20:25:56

質問

デリゲートがどのように機能するか知っていますし、どのように使用できるかも知っています。

しかし、どのように作成すればよいのでしょうか?

どのように解決するのですか?

Objective-Cのデリゲートは、オブジェクトに割り当てられた delegate プロパティを使用します。デリゲートメソッドを実装するクラスを定義し、そのクラスがデリゲートプロトコルを実装しているとマークします。

例えば UIWebView . もし、そのデリゲートの webViewDidStartLoad: というメソッドがあれば、このようなクラスを作成することができます。

@interface MyClass<UIWebViewDelegate>
// ...
@end

@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView { 
    // ... 
}
@end

そして、MyClass のインスタンスを作成し、それを Web ビューのデリゲートとして割り当てることができます。

MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;

について UIWebView 側では、おそらくこのようなコードがあり、デリゲートが webViewDidStartLoad: メッセージは respondsToSelector: を作成し、必要に応じて送信してください。

if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
    [self.delegate webViewDidStartLoad:self];
}

デリゲートプロパティ自体は、通常、次のように宣言されます。 weak (ARCの場合)または assign (ARC以前の)オブジェクトのデリゲートは、しばしばそのオブジェクトへの強い参照を保持しているので、retainループを避けることができます。(例えば、ビューコントローラは、それが含むビューのデリゲートであることがよくあります。)

クラスのデリゲートを作成する

独自のデリゲートを定義するには、そのメソッドをどこかで宣言する必要があります。 プロトコルに関するApple Docs . 通常、正式なプロトコルを宣言します。UIWebView.hから言い換えると、このような宣言になります。

@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end

これは、デリゲートのための特別な型を作成するため、インターフェースや抽象ベースクラスと類似しています。 UIWebViewDelegate この場合 デリゲート実装者はこのプロトコルを採用しなければならないだろう。

@interface MyClass <UIWebViewDelegate>
// ...
@end

そして、プロトコルにあるメソッドを実装します。プロトコルの中で次のように宣言されたメソッドについては @optional (ほとんどのデリゲートメソッドと同様) でチェックする必要があります。 -respondsToSelector: で特定のメソッドを呼び出す前に、そのメソッドの

ネーミング

デリゲートメソッドの名前は通常、デリゲートクラス名で始まり、デリゲートオブジェクトを最初のパラメータとして取ります。また、will-, should-, did- という形式がよく使われます。つまり webViewDidStartLoad: (最初のパラメータはウェブビュー) ではなく loadStarted (パラメータを取らない)などです。

スピードの最適化

デリゲートがセレクタに応答するかどうかをメッセージのたびにチェックする代わりに、デリゲートが設定されたときにその情報をキャッシュすることができます。これを行う非常にクリーンな方法のひとつは、以下のようにビットフィールドを使用することです。

@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end

@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end

@implementation Something {
  struct {
    unsigned int didFinishLoadingItem:1;
    unsigned int didFailWithError:1;
  } delegateRespondsTo;
}
@synthesize delegate;

- (void)setDelegate:(id <SomethingDelegate>)aDelegate {
  if (delegate != aDelegate) {
    delegate = aDelegate;

    delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
    delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
  }
}
@end

次に、ボディにおいて、デリゲートがメッセージを処理することを確認するために delegateRespondsTo 構造体を送信するのではなく -respondsToSelector: を何度も何度も繰り返す。

非公式代議員

プロトコルが存在する以前は、一般的に カテゴリー オン NSObject を使用して、デリゲートが実装可能なメソッドを宣言します。例えば CALayer は、今もこのままです。

@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end

これはコンパイラに、任意のオブジェクトが displayLayer: .

その場合、同じように -respondsToSelector: は、このメソッドを呼び出すために、上で説明したようなアプローチをとります。デリゲートはこのメソッドを実装し delegate プロパティを指定すれば、それで完了です(プロトコルに準拠することを宣言する必要はありません)。この方法は Apple のライブラリでは一般的なものですが、新しいコードでは上記のようなより現代的なプロトコルアプローチを使うべきでしょう。 NSObject (オートコンプリートが使いにくくなる)、コンパイラがタイプミスや同様のエラーを警告しにくくなります。