delegateデザインパターンによる情報通知(Objective-C)


Objective-Cには(例えばC#のように)delegateという名称の専用機能があるわけではない。delegateは情報通知のためのデザインパターンの1つであり、Objective-Cにおいては、慣例的にdelegateという変数名でプロパティ宣言して使うことが多い。

登場人物は情報通知元と情報通知先の2人であるが、基本的には、通知元は通知先のことを知る必要はないということがポイントであり、再利用性の高いコードのために重要な点だ。

## Objective-Cにおけるコーディング例
**環境**
- Xcode 6.4
- Objective-C 2.0
- iOS 8.4

[GitHub](https://github.com/sushichop/ObjCSampleDelegate)にはソース一式をあげておきました。
また、Swiftにおける例は[こちら](http://qiita.com/sushichop/items/1bdc3e11f0398b627267)に書きました。

### 通知元(SenderViewController)

例として実装が必須なもの(required)とそうでない(optional)メソッドをそれぞれ定義している。
**本来のデザインパターンの意義から考えると、個人的には、requiredよりもoptionalなメソッドのほうが有用性が高いと思っている。**

requiredなメソッドについては、プロトコル適合クラス（通知先、今回の例ではReceiverViewController)において実装しないとXcodeがwarningを出して知らせてくれる。一方、optionalなメソッドについてはwarningは発生しないが、アプリがクラッシュしないように、respondsToSelector:メソッドを使って、メソッドを実装しているかをコード内でチェックする必要がある。


```objectivec:SenderViewController.h
@import UIKit;

@class SenderViewController;

@protocol SenderViewControllerDelegate <NSObject>

@required
- (void)senderViewController:(SenderViewController *)senderViewController didSendRequiredInfo:(NSDictionary *)info;

@optional
- (void)senderViewController:(SenderViewController *)senderViewController didSendOptionalInfo:(NSDictionary *)info;

@end

@interface SenderViewController : UIViewController

@property (nonatomic, weak) id<SenderViewControllerDelegate> delegate;

@end
```

```objectivec:SenderViewController.m
#import "SenderViewController.h"

@interface SenderViewController ()

@end

@implementation SenderViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - IBAction

- (IBAction)goBack:(id)sender
{
    // Xcode checks the method is implemented or not.
    [self.delegate senderViewController:self didSendRequiredInfo:@{@"message": @"this is required", @"score": @3}];
    
    // You have to check the method is implemented or not.
    if ([self.delegate respondsToSelector:@selector(senderViewController:didSendOptionalInfo:)]) {
        [self.delegate senderViewController:self didSendOptionalInfo:@{@"message": @"this is optional", @"score": @100}];
    }
    
    [self dismissViewControllerAnimated:YES completion:nil];
}

@end

```


### 通知先(ReceiverViewController)

プロトコルを適合(準拠)するクラス。通知元とは逆に、通知先は通知元が提供するメソッド等をよく理解しておく必要がある。

```objectivec:ReceiverViewController.h
@import UIKit;

@interface ReceiverViewController : UIViewController

@end
```

```objectivec:ReceiverViewController.m
#import "ReceiverViewController.h"
#import "SenderViewController.h"

@interface ReceiverViewController () <SenderViewControllerDelegate>

@end

@implementation ReceiverViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Segue

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"SenderView"]) {
        SenderViewController *senderViewController = (SenderViewController *)segue.destinationViewController;
        senderViewController.delegate = self;
    }
}

#pragma mark - SenderViewControllerDelagate

- (void)senderViewController:(SenderViewController *)senderViewController didSendRequiredInfo:(NSDictionary *)info
{
    NSLog(@"info: %@", info);
}

- (void)senderViewController:(SenderViewController *)senderViewController didSendOptionalInfo:(NSDictionary *)info
{
    NSLog(@"info: %@", info);
}

@end
```