Objective-CにはProtocolDelegateObserverなどメッセージのやりとりをする実装方法がありますが、それらと同じ位便利なNotificationを紹介します。

これを使えば、不特定多数(メッセージを送信する側は誰が受け取るが把握しないため)のオブジェクトに対して任意のメッセージを送る事が出来ます。

NSNotificationの使いどころ

僕は普段、Objective-Cにおいてメッセージのやりとりをするときに、ProtocolDelegateを使います。

けど、今回作ったものの実装で、不特定多数のオブジェクトが同じ値を求めるときにいちいちDelegateを配列で保持するのもなと思っていました。

そこで使ったのがNSNotificationというやつです。

簡単な例を紹介します。

1
2
3
4
5
6
7
8
9
- (void)viewDidLoad
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void)didEnterBackground:(NSNotification *)notification
{
NSLog(@"didEnterBackground:");
}

このソースコードはアプリがバックグラウンドに遷移した後にdidEnterBackground:を呼び出してくださいとNSNotificationCenterに登録しているサンプルになります。

実際に動かすとバックグラウンドに移動した時点でログが出力されます。

これはどのクラスで実装しても同じタイミングでログが出力されます。

この様に、なにかの後のCallbackとはまた違った形でメッセージを受け取りたいときに使うものと認識しています。

NSNotificationの使用方法(受信編)

ほぼ先程のサンプル通りになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)viewDidLoad
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void)didEnterBackground:(NSNotification *)notification
{
NSLog(@"didEnterBackground:");
}
- (void)deallo
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self];
}

通知してもらうようにオブジェクトの登録

addObserver:selector:name:object:を使ってオブジェクトを登録します。

以下は各引数の説明です。

  • addObserver

    受信者の設定です。

  • selector

    受信したときに呼び出されるメソッドです。

  • name

    通知の名前です。

    大体がUIApplicationDidEnterBackgroundNotificationの様に定数を用意されています。

    通知の送信する側を実装する場合は定数を用意するのが好ましいと思います。

  • object

    送信者の指定です。

まあ、とってもシンプルです。

登録の解除

これは絶対に忘れないでください。

1
2
3
4
5
- (void)deallo
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self];
}

上記の部分になります。

removeObserver:を忘れてしまうと、登録したオブジェクトが破棄されている状態で通知(メッセージを送信)しようとして、アプリがクラッシュしてしまいます。

dealloc以外にもdidReceiveMemoryWarningなどでやる必要があります。

なので、どの状態では通知を受け取らないようにするか、どの状態では受け取りたいかをはっきりさせておく必要があります。

NSNotificationの使用方法(送信編)

これも簡単に実装できます。

以下のような感じです。

NASampleNotification.h
1
2
3
4
5
6
@interface NASampleNotification : NSObject
extern NSString *const NASampleNotificationDidChangeHogeNotification;
extern NSString *const NASampleNotificationDidChangeHogeUserInfoHogeKey;
- (void)setHoge:(NSString *)hoge;
- (NSString *)hoge;
@end
NASampleNotification.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@interface NASampleNotification()
@property (nonatomic, copy) NSString *hoge;
- (void)notification;
@end
@implementation NASampleNotification
static NSString *const NASampleNotificationDidChangeHogeNotification = @"NASampleNotificationDidChangeHogeNotification";
static NSString *const NASampleNotificationDidChangeHogeUserInfoHogeKey = @"hoge";
- (void)setHoge:(NSString *)hoge
{
self.hoge = hoge;
NSDictionary *userInfo = @{NASampleNotificationDidChangeHogeUserInfoHogeKey: self.hoge};
[self notification];
}
- (NSString *)hoge
{
return self.hoge;
}
#pragma mark - private method
- (void)notification
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationNama:NASampleNotificationDidChangeHogeNotification object:self userInfo:userInfo];
}
@end

この程度だったら@property使えよって思われると思いますが、サンプルなんでシンプルにしたいと思い、このような実装を選びました。

このクラスを使って値を受け取ります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#import "NASampleNotification.h"
- (void)viewDidLoad
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(didChangeHoge:) name:NASampleNotificationDidChangeHogeNotification object:nil];
}
- (void)didChangeHoge:(NSNotification *)notification
{
// hogeの値は以下の二通りで取得できます
/*
NSSampleNotification *sample = (NSSampleNotification *n)[notification object];
NSString *hoge = sample.hoge;
*/
NSDictionary *userInfo = (NSDictionary *)[notification userInfo];
NSString *hoge = userInfo[NASampleNotificationDidChangeHogeUserInfoHogeKey];
}
- (void)deallo
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self];
}

という感じです。

欲しい値の取得方法は二通りあります。

どっちを使うかは好みによるかと思います。

終わりに

この様にProtocolDelegate程ではありませんが、簡単に使えるので、実装する機会がありましたらぜひ使ってみてください。

以上になります。