Objective-Cではカテゴリを使って既存のクラスを拡張してメソッドを追加できるが、メソッドだけでなくプロパティを追加することもできる。
例えばこんな風にUIView
のカテゴリを新規作成し、ヘッダーにプロパティを定義する。今回はNSString
にした。
// UIView+PropertySample.h
#import <UIKit/UIKit.h>
@interface UIView (PropertySample)
@property (nonatomic, weak) NSString *value;
@end
これだけ書くと以下のような警告が出る。
Property 'value' requires method 'value' to be defined - use @dynamic or provide a method implementation in this category
Property 'value' requires method 'setValue:' to be defined - use @dynamic or provide a method implementation in this category
@dynamicを使うか、value
とsetValue:
を定義しろと言われている。@dynamic
はアクセサを自動生成しないという指定だった気がするから、今回は@dynamic
を書いた上で、value
/setValue:
を定義した。
// UIView+PropertySample.m
#import "UIView+PropertySample.h"
#import <objc/runtime.h>
@implementation UIView (PropertySample)
@dynamic value;
- (void)setValue:(NSString*)value
{
objc_setAssociatedObject(self, _cmd, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString*)value
{
return objc_getAssociatedObject(self, @selector(setValue:));
}
@end
objc_getAssociatedObject
とobjc_setAssociatedObject
という関数を使うためにobjc/runtime.h
をインポートしておく。
これらの関数は既存のオブジェクトに他のオブジェクトを関連付けるというもので、ここではプロパティを擬似的に表現するために使っている。
ポイントはsetterの_cmd
とgetterの@selector(setValue:)
。同じオブジェクトに複数のオブジェクトを関連付けられるため、どの関連付けに対するset/getなのかを明確にするためのキーが必要になる。ここではsetValue:
のアドレスを用いている。キーには一意なポインタが必要であり、セレクタを使うのは理にかなっているような気がする。
ひとまずこれでカテゴリに使ったプロパティの追加ができた。