クラスクラスタリングは、Foundation Frameworkで広く使われているデザインパターンです。
NSArray *arr = [NSArray arrayWithObjects:@"foo",@"bar", nil];
NSLog(@"arr class:%@", [arr class]);
// output: __NSArrayI
明らかに__NSArrayIはプライベートクラスなので、このクラスのヘッダーファイルを見てください。
@interface __NSArrayI : NSArray {
unsigned int _used;
}
//...
NSArrayIはNSArrayを継承していることがわかります。NSNumberを例にとると、NSNumberはInt/Float/Doubleなどの様々なタイプの数値を格納できることは周知の通りです:
最初は問題ないように見えますが、サブクラスがたくさんある場合は、このようになります:
多くのクラスを覚えなければならないユーザーにとって、これは明らかに不便です。Numberを抽象基底クラスとし、サブクラスはそれぞれアクセス・メソッドを実装し、基底クラスに複数の初期化メソッドを定義します:
あとはクラスを覚えておくだけです。NSNumberの初期化擬似コードは大体こんな感じです:
- (id)initWithBool
{
return [[__NSCFBoolean alloc]init];
}
- (id)initWithLong
{
return [[__NSCFNumber alloc]init];
}
//...
iOSプロジェクトでの応用
アプリを開発していると、パフォーマンスや動作は全く同じなのに、データソースが異なるという状況によく遭遇します。たとえば Petals アプリを例にとると、同じ滝が私のお気に入りの写真から流れてきたり、 お絵かきボードの下にある写真から流れてきたり、 ユーザーの写真から流れてきたり......。これらの表現ごとに新しいControllerを作成し、そのControllerで初期化すると、冒頭で述べたような問題にぶつかります。これは、クラスクラスタリングで簡単に対処できます。たとえば
@implementation HBWaterfallViewController
- (id)initWithLiked
{
return [[HBLikedViewController alloc]init];
}
- (id)initWithBoardID:(NSInteger)boardID
{
return [[HBBoardViewController alloc]initWithBoardID:boardID];
}
#pragma mark - 一般的なアプローチ
- (PSUICollectionViewCell *)collectionView:(PSUICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
// ...
}
// ...
#pragma mark - 各サブクラスが実装するメソッド
- (void)fetchMoreData
{
NSAssert(NO, @"サブクラスはこのメソッドを実装する必要がある」)。;
}
[[HBWaterfallViewController alloc]initWithBoardID:9527] [[HBWaterfallViewController alloc]initWithLiked]このように使用します。新しいDataSourceがある場合は、新しい初期化メソッドを追加するだけです。ユーザーの場合は、ヘッダファイルを開き、initの先頭にあるメソッドを見てください。
別の例では、今、多くのアプリケーションは、iOS6とiOS7の両方の世話をする必要があり、異なるシステム別のイメージリソースをロードする必要性のパフォーマンスでは、最も簡単な方法は、このようなif/elseの判断の様々です:
if ([[UIDevice currentDevice]systemMajorVersion] < 7)
{
/* iOS 6 and previous versions */
}
else
{
/* iOS 7 and above */
}
十分にエレガントではありませんが、クラスクラスタリングのアイデアを利用してif/else判定を取り除き、ビューの特定の要素に関係のないコードをベースクラスに置き、システムのバージョンに関係のあるコードを2つのサブクラスに分割し、それぞれのクラスで適切なリソースをロードすることができます。
/* TestView.h */
@interface TestView: UIView
/* Common method */
- ( void )test;
@end
/* TestView.m */
@implementation TestView
+ (id)alloc
{
if ([self class]== [TestView class])
{
if ([[UIDevice currentDevice] systemMajorVersion] < 7)
{
return [TestViewIOS6 alloc];
}
else
{
return [TestViewIOS7 alloc];
}
}
else
{
return [super alloc];
}
}
- ( void )test
{}
@end
/* TestViewIOS6.m */
@implementation TestViewIOS6: TestView
- (void)drawRect: (CGRect)rect
{
/* Custom iOS6 drawing code */
}
@end
/* TestViewIOS7.m */
@implementation TestViewIOS7
- (void)drawRect: (CGRect)rect
{
/* Custom iOS7 drawing code */
}
@end
まとめ
クラス・クラスターの本質は、実はです。 クラス・クラスターは、NSArrayやNSMutableArrayのように複数の基底クラスを持つこともでき、後者は前者を継承したものです。これは、いくつかの「ほとんど同じ」問題に対してうまく機能する傾向があります。





