14 | 1月 | 2014 | HMDT Blog

カテゴリー : 2014年 1月14日

クラスクラスタのプリミティブメソッドの見つけ方


「The Complete Friday Q&A: Volume I」を読んでます。今日読んだのは、「Subclassing Class Clusters」の項。

Objective-CというかFoundationには、クラスクラスタと呼ばれる仕組みがありまして。これは、同じAPIを使って、異なる実装を自動的に使い分けるための仕組みです。

たとえば、NSStringはクラスクラスタとして提供されています。それが何を意味しているかというと、NSStringのインスタンスを作っても、実はそれはNSStringじゃなくて、NSCFStringやNSPathreStore2っていうクラスになっているんですね。いわば、NSStringは抽象クラスで、自動的に効率のいい実装を持つクラスが選択されています。デザインパターンでいうと、Factory Methodパターンみたいなものです。言語仕様でのサポートというよりは、Foundationで実現しているものです。

クラスクラスタは、提供されているものを使う分には楽しく使えるんですけど、サブクラス作ろうとすると、とたんに面倒になります。まずはまるのが、既存のNSStringの挙動を変えてやろう!と思ってNSStringのサブクラスを作ると、うまくいきません。これは、具体的な実装である、NSCFStringなどに対してサブクラス化やメソッド差し替えを行わないといけないです。

その次には、自前の実装クラスを作って、クラスクラスタに追加してやろう!ということを考えるでしょう。この場合は、NSStringのすべてのメソッドを実装する必要はなく、プリミティブメソッドと呼ばれるものだけをやればいい、ということになっています。他のメソッドは、最終的にはこのプリミティブメソッドを呼び出して、具象クラスにアクセスします。

で、ここからがFriday Q&Aのネタなのですが、このプリミティブメソッドをどうやって見つけるのか?基本は、APIドキュメントを読みます。確かに書いてあるのですが、特別なセクションがある訳ではなく、普通の説明の流れにちょろっと書いてあるだけなので、見落としがちです。

そこで紹介されているもう1つの方法が、ヘッダファイルのinterfaceセクションに宣言されているメソッドがプリミティテブである、というものです。それ以外のものは、カテゴリとして宣言されているのです。これも、言語仕様ではなくてマナーの範疇なので100% sureではないですが、充分に役に立ちそうです。

その観点が面白いので、めぼしいクラスのintefaceで宣言されているメソッドを抜き出してみました。

NSArray

- (NSUInteger)count;
- (id)objectAtIndex:(NSUInteger)index;

NSMutableArray

- (void)addObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;

NSAttributedString

- (NSString *)string;
- (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range;

NSMutableAttributedString

- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str;
- (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range;

NSData

- (NSUInteger)length;
- (const void *)bytes NS_RETURNS_INNER_POINTER;

NSDate

- (NSTimeInterval)timeIntervalSinceReferenceDate;

NSDictionary

- (NSUInteger)count;
- (id)objectForKey:(id)aKey;
- (NSEnumerator *)keyEnumerator;

NSMutableDictionary

- (void)removeObjectForKey:(id)aKey;
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;

NSString

- (NSUInteger)length;
- (unichar)characterAtIndex:(NSUInteger)index;

NSMutableString

- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)aString;

NSValue

- (void)getValue:(void *)value;
- (const char *)objCType NS_RETURNS_INNER_POINTER;

 

なるほど。これらがプリミティブ的に扱われている訳ですね。NSDateがtimeIntervalSinceReferenceDateだけ宣言されているのとか、面白いですね。