2013/04/09更新

[XCODE] Objective-Cでdelegateを実装する方法

このエントリーをはてなブックマークに追加      

こんにちは、@yoheiMuneです。
今日は、Objective-Cに存在するProtocolという機能を利用して、Delegateパターンを実装する方法をブログに書きたいと思います。

画像



Protocolとは

ProtocolとはObjective-Cに用意されt氏組で、JavaのInterfaceに似た仕組みを提供します。
「似ている」という表現を用いるのは、JavaのInterfaceと異なる点が多くあるからです。

違うポイント Interfaceはis-aの関係、Protocolはconfirm toの関係

Protocolでは、定義したメソッドをすべて実装する必要が必ずしもありません。
以下の定義のように@optionalと定義したメソッドは、実装するか否かを選ぶことができます。
@protocol LoginServiceDelegate <NSObject>
  // @requiredが付いたメソッド(@requiredは省略可)は、実装が必須
  @required
    -(void)loginService:(LoginService *)service willStartLogin;

  // @optionalは実装してもしなくても良い
  @optional
    -(void)loginService:(LoginService *)service didEndLogin;

@end

上記のように必ずしも実装しなくてよいというものなので、JavaのIntervaceを実装するクラスがis-aの関係に対して、 Objective-CのProtocolは、「confirm to、adapt to(=適応する)」の関係となるようです。
なので名前がプロトコルとなっていて、「プロトコルに適応する」という表現をとるのかなぁと思います。


Protocolを利用するとDelegateを綺麗に実装することが出来ます。
以下の例では、ViewControllerからServiceを呼び出して、適宜タイミングでServiceがViewControllerに処理を委譲するサンプルを紹介します。



Protocolを利用したDelegateの実装サンプル

以下の例では、ViewControllerからServiceにログイン(OAuth認証など)処理を要求して、 ログイン処理実施前と完了時に、ServiceがViewControllerに適宜処理を委譲するサンプルです。

処理を委譲する側(Service)の実装方法

Service側では、Protocolの定義と、処理中でのDelegateの呼び出しを実装します。
// まず最初にDelegate用のプロトコルを宣言します
@protocol LoginServiceDelegate;

// サービスのインターフェースを定義します
@interface LoginService : NSObject {
  // フィールド変数で、デリゲートを保持します
  id<LoginServiceDelegate> delegate;
}
@property(nonatomic, retain) id<LoginServiceDelegate> delegate;
-(void)login;
@end

// プロトコルの定義を行います
@protocol LoginServiceDelegate <NSObject>
  @required
    -(BOOL)loginService:(LoginService *)service willStartLogin;
  @optional
    -(void)loginService:(LoginService *)service didEndLogin:(NSError *)error;
@end

これで、プロトコルの定義、Serviceのインターフェース定義が完成です。
次にServiceの具体的な実装例です。
#import "LoginService.h"
@implementation LoginService
@synthesize delegate;

-(void)login {
  // ログインする前に、delegateメソッドを呼び出します
  BOOL startLogin = [delegate loginService:self willStartLogin];
  if (startLogin == NO) {
    // delegateの処理結果でNGとなった場合には、処理しない
    return;
  }

  // ログイン処理を行います。以下のメソッドは仮です。
  [self doLoginWithCallback:^(LoginInfo *info, Error *error) {
    // @optional指定しているdelegateのメソッドは実装されていない可能性があるので、
    // 実装確認をしてから呼び出します。
    if ([delegate respondsToSelector:@selector(loginService:didEndLogin:)]) {
        [delegate didEndLogin:self didEndLogin:error];
    }
  }];
}
@end

delegateを呼び出す部分は、実装確認する必要がありますが、 それ以外は普通のメソッド呼び出しで実装できるので簡単な感じです。



Delegateを利用する側の実装

Delegateを利用する側の実装は、CocoaフレームワークのDelegateを利用する場合と同じように実装することが出来ます。

(.hファイル)
#import LoginService
// Protocolを実装することを宣言します
@interface FirstViewController : UIViewController <LoginServiceDelegate> {
  LoginService *loginService;
}
@end

(.mファイル)
- (void)viewDidLoad {
  // ログイン処理を呼び出します
  loginService = [[LoginService alloc] init];
  loginService.delegate = self;
  [loginService login];
}

// LoginServiceDelegateを実装します
#pragma mark - LoginServiceDelegate
-(BOOL)loginService:(LoginService *)service willStartLogin {
  NSLog(@"ログインが始まるよー");
  return YES;
}
-(void)loginService:(LoginService *)service didEndLogin:(NSError *)error {
  NSLog(@"ログインが終わったよー。 eror = %@", error);
}
Delegateを使う側は、Cocoaでよく使う形式と同じなので利用しやすいかもです。



最後に

上記のDelegateを実装できると、ViewControllerとServiceが相互参照せずに処理の受け渡しが出来るので、便利です。
なお上記の実装例以外にも、メソッド単位にコールバックを指定することで処理の受け渡しをすることも可能です。
詳細は下記リンクをご覧頂けると助かります。

- [XCODE] 処理が終わった際のコールバックをSelectorやBlockで実装する方法


最後までご覧頂きましてありがとうございました。





こんな記事もいかがですか?

RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。