blog

iOS開発におけるクラス・クラスター

クラス・クラスターの本質は、実は抽象ファクトリーです。 クラス・クラスターは、NSArray、NSMutableArrayのように複数の基底クラスを持つこともできます。これは、いくつかの「多かれ少なか...

Sep 8, 2016 · 5 min. read
シェア

クラスクラスタリングは、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のように複数の基底クラスを持つこともでき、後者は前者を継承したものです。これは、いくつかの「ほとんど同じ」問題に対してうまく機能する傾向があります。

Read next

新しい無線ネットワークは、より高いユーザー容量をサポートする

多くの企業が無線ネットワークの容量問題を経験しています。無線デバイスや広帯域アプリケーションの急増に伴い、ネットワークの速度とデータ容量を保証することはますます難しくなっています。増大する需要に対応するため、WLAN 管理者は、既存のチャネルの効率改善と、新しい周波数の導入による帯域幅負荷のシフトという 2 つの準備を行う必要があります。この2つの戦略により、802.11nのピークに近づいている企業が窮地を脱することが期待されます。

Sep 8, 2016 · 3 min read