こちらの記事からの移植です。タイトルを付けたかったので。
Effective Objective-C 2.0を読んでいて見つけた方法。
ただし、書籍中ではできるだけ使わないように、との注意書きもあったのでメモとして書いています。
Associated Objectって?
Associate Objectは既存のクラスにカスタムデータを追加する仕組みです。
JavaScriptであればいくらでもプロパティが追加できますが、それと同様にして動的にカスタムデータを追加できるようです。
この方法には、ランタイムであるC関数を利用して、以下のようにします。
#import <objc/runtime.h>
static void *HogeAlertViewKey = "HogeAlertViewKey";
- (void)alertTest
{
UIAlertView *alert = [UIAlertView alloc] initWithTitle:@"Title"
message:@"message"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"Other1", @"Other2", nil];
void (^blk)(NSInteger) = ^(NSInteger buttonIndex) {
if (buttonIndex == 0) {
[self doCancel];
}
else {
[self doSomething];
}
};
objc_setAssociatedObject(alert, HogeAlertViewKey, blk, OBJC_ASSOCIATION_COPY);
[alert show];
}
- (void)alertView:(UIAlertView *)alertView
clickedButtonAtIndex:(NSInteger)buttonIndex
{
void (^blk)(NSInteger) = objc_getAssociatedObject(alertView, HogeAlertViewKey);
blk(buttonIndex);
}
解説
やっていることは単純で、生成したUIAlertView
のインスタンスに動的に、カスタムデータとしてblockを追加しています。
そして、デリゲートメソッド内でそれを取り出し、押されたbuttonIndex
を引数にして呼び出している、というわけです。
こうすることによってAlertViewの生成と処理を同じ場所に書くことができ、またデリゲートメソッド内で複数のAlertViewに対する処理を書かなくて済む、というメリットがあります。
ただ、AlertViewのサブクラスを作ってblockで処理を指定できるようにするか、BlockKitを使う方法のほうがよりスマートだとは思います。
あくまでイチ手法としてメモしておきます。
Associated Objectで使える関数郡
関数プロトタイプ | 説明 |
---|---|
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy) | 指定したキーとポリシーのもとで、Associated Objectをセットする |
id objc_getAssociatedObject(id object, void *key) | オブジェクトの指定したキーを持つAssociated Objectの値を返す(カスタムデータの取り出し) |
void objc_removeAssociatedObject(id object) | オブジェクトからすべてのAssociated Objectを削除する |
void *keyについて
サンプルコードではstatic void *HogeAlertViewKey = "HogeAlertViewKey";
としてグローバル領域にvoid *
型の変数を宣言していますが、以下のようにすることでも対応が可能なようです。
それ専用の変数などを作る必要がないのでいいかも。
- (void)alertTest
{
// 中略
objc_setAssociatedObject(alert, @selector(alertView:clickedButtonAtIndex:), blk, OBJC_ASSOCIATION_COPY);
}
////////////////////////////////////////
- (void)alertView:(UIAlertView *)alertView
clickedButtonAtIndex:(NSInteger)buttonIndex
{
void (^blk)(NSInteger) = objc_getAssociatedObject(alertView, _cmd);
}
このように、@selector(anyMethod)
と、objc_getAssociatedObject
を実行するメソッド(anyMethod)内で_cmd
を参照することで、どちらも同じキーを参照することができます。
Associated Objectのタイプ
Associated Objectのタイプは、通常のObjective-Cと同様、以下のようにポリシーを設定できます。
Associated Objectのタイプ | 同じ意味の@property属性 |
---|---|
OBJC_ASSOCIATION_ASSIGN | assign |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | nonatomic, retain |
OBJC_ASSOCIATION_COPY_NONATOMIC | nonatomic, copy |
OBJC_ASSOCIATION_RETAIN | retain |
OBJC_ASSOCIATION_COPY | copy |