Objective-C でデリゲートを作成するにはどうすればいいですか? 質問する

Objective-C でデリゲートを作成するにはどうすればいいですか? 質問する

私はデリゲートがどのように機能するかを知っており、それをどのように使用できるかも知っています。

しかし、どうやって作成するのでしょうか?

ベストアンサー1

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 以前) 宣言されます。(たとえば、ビュー コントローラは、それに含まれるビューのデリゲートであることが多いです。)

クラスの代表者を作成する

独自のデリゲートを定義するには、そのメソッドをどこかで宣言する必要があります。プロトコルに関する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:(最初のパラメーターは Web ビュー) ではなくloadStarted(パラメーターなし) となります。

速度の最適化

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

@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(オートコンプリートの有用性が低下します)、コンパイラがタイプミスや同様のエラーについて警告しにくくなるためです。

おすすめ記事