2

サブクラスによって「オーバーライド」できるクラス変数について、Objective-C で適切なパターンは何でしょうか?

通常のクラス変数は、通常、クラス メソッドとして定義された公開されたアクセサーと共にファイル ローカルな静的変数を使用して、Objective-C でシミュレートされます。

ただし、これはクラス変数と同様に、クラスとそのすべてのサブクラスの間で値が共有されることを意味します。場合によっては、サブクラスがそれ自体の値のみを変更すると興味深いことがあります。これは通常、構成にクラス変数が使用される場合に当てはまります。

例を次に示します。一部の iOS アプリでは、特定の共通抽象スーパークラス (注釈) の多くのオブジェクトがあり、それらは多くの具体的なバリエーション (サブクラス) になります。すべての注釈はラベルでグラフィカルに表現され、ラベルの色はその注釈の特定の種類 (サブクラス) を反映する必要があります。したがって、すべての Foo 注釈には緑色のラベルが必要であり、すべての Bar 注釈には青色のラベルが必要です。各インスタンスにラベルの色を保存するのは無駄です (実際には、多くのオブジェクトがあり、実際の構成データ (各インスタンスに共通) は単一の色よりもはるかに大きいため、おそらく不可能です)。

実行時に、ユーザーはすべての Foo 注釈に赤いラベルを付けるように決定できます。等々。

Objective-C ではクラスは実際のオブジェクトであるため、Foo クラス オブジェクトに Foo ラベルの色を格納する必要があります。しかし、それは可能ですか?この種のものにはどのようなパターンが適していますか? もちろん、クラスをその構成値にマッピングするある種のグローバル辞書を定義することは可能ですが、それはちょっと見苦しいものです。

4

2 に答える 2

1

もちろん、クラスをその構成値にマッピングするある種のグローバル辞書を定義することは可能ですが、それはちょっと見苦しいものです。

なぜこれが醜いと思いますか?[self className]辞書のキーとして使用できるため、非常に単純なアプローチです。また、ディクショナリを NSUserDefaults に格納するだけでよいため (プロパティ リスト オブジェクトのみが含まれている場合)、永続化するのも簡単です。superclassまた、値を持つクラスが見つかるまでメソッドを呼び出して、各クラスのデフォルト値をスーパークラスの値にすることもできます。

+ (id)classConfigurationForKey:(NSString *)key {
    if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
    Class c = [self class];
    id value = nil;
    while(value == nil) {
        NSDictionary *classConfig = [_configurationDict objectForKey:[c className]];
        if(classConfig) {
            value = [classConfig objectForKey:key];
        }
        c = [c superclass];
    }
    return value;
}
+ (void)setClassConfiguration:(id)value forKey:(NSString *)key {
    if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
    NSMutableDictionary *classConfig = [_configurationDict objectForKey:[self className]];
    if(classConfig == nil) {
        classConfig = [NSMutableDictionary dictionary];
        [_configurationDict setObject:classConfig forKey:[self className]];
    }
    [classConfig setObject:value forKey:key];
}

この実装では、最上位のスーパークラスを超えないようにするためのチェックが行われないため、無限ループを回避するために、そのクラスに値があることを確認する必要があります。

プロパティ リストに格納できないオブジェクトを格納する場合は、辞書にアクセスするときに前後に変換するメソッドを使用できます。labelColorUIColor オブジェクトであるプロパティにアクセスする例を次に示します。

+ (UIColor *)classLabelColor {
    NSData *data = [self classConfigurationForKey:@"labelColor"];
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
+ (void)setClassLabelColor:(UIColor *)color {
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:color];
    [self setClassConfiguration:data forKey:@"labelColor"];
}
于 2011-08-23T22:56:11.807 に答える
0

ここでの私の答えが役立つかもしれません:

iOS アプリのスタイリングで推奨される方法は何ですか?

その場合、注釈はスタイルへの参照を保持するだけで (たとえば、スタイルごとに 1 つだけ必要です)、スタイル全体のポインターのサイズは悪くありません。いずれにせよ、その投稿はあなたにいくつかのアイデアを与えるかもしれません。

アップデート

Jean-Denis Muys: それは私の質問のサンプル ユース ケースに対応していますが、私の質問自体 (クラス インスタンス変数をシミュレートするパターン) には対応していません。

そうです、あなたの例があなたの問題をどれほど密接にモデル化しているかわからなかったので、それについてコメントすることを検討しました。

より一般的で再利用可能なソリューションについては、グローバルデータが自明でない場合(OPで述べたように)、おそらくスレッドセーフなグローバル辞書を作成するだけです。+initializeクラスメソッドを導入することで、それを取り込むか、遅延させることができます。次に、いくつかのカテゴリを追加してNSObject、静的データにアクセスして変更することができます。構文を簡単にするためにこれを行います。

このアプローチの良いところは、任意のプログラムで再利用できることだと思います (たとえ書くのが見苦しく複雑に見えても)。ロックが多すぎる場合は、辞書をプレフィックスで分割するか、クラスが参照を保持する単純なスレッドセーフな辞書を作成することをお勧めします。その後、objc ランタイムを介してインスタンス変数を合成し、それを格納してインスタンス メソッドを宣言できます。をクリックしてアクセスします。クラス メソッドはグローバル データ インターフェイスを直接使用する必要があります。

于 2011-08-23T22:12:55.323 に答える