3

NSMutableDictionaryゲームデータ(スコア、設定など)を保存するためのプロパティ を使用することを計画していました。

@property (nonatomic, copy) NSMutableDictionary *gameData;

プロパティに「mutablecopy」オプションがない理由を調査する際に、このディスカッションを見つけました。これに対して、受け入れられた回答は次のように述べています。

それを行う正しい方法は、可変配列をプロパティにしないことです

それでは、最新の Objective-C で変更可能なコレクションをプロパティとして処理する最良の方法は何ですか?

4

3 に答える 3

7

カプセル化を破るように見えるため、クラスがコレクション オブジェクトの可変プロパティを公開するパターンはほとんどありません。

それにもかかわらず、それが理にかなっている場合があります。たとえば、内部で管理されているコレクションの KVO プロキシを返すことができます ( KVC のmutableArrayValueForKey:を使用)。

このような場合、getter をプロパティとして宣言するかプレーン メソッドとして宣言するかは問題ではありません。ただし、プロパティの場合は、プロパティであってはなりませんcopy

これをもう少し明確にするために、ここに例を示します。単一の public getter を宣言するクラスがあります。

@interface Foo : NSObject

@property (strong, readonly) NSMutableArray *publicBars;

@end

この実装は、次の名前のカプセル化された非公開の可変配列を管理します_bars

@implementation Foo
{
    NSMutableArray *_bars;
}

この配列の要素は、KVC to-many アクセサーによって公開されます。

- (NSUInteger)countOfBars
{
    return [_bars count];
}

- (id)objectInBarsAtIndex:(NSUInteger)idx
{
    return _bars[idx];
}

- (void)insertObject:(id)object inBarsAtIndex:(NSUInteger)idx
{
    if (_bars == nil)
        _bars = [NSMutableArray array];
    [_bars insertObject:object atIndex:idx];
}

- (void)removeObjectFromBarsAtIndex:(NSUInteger)idx
{
    [_bars removeObjectAtIndex:idx];
}

ここで面白い部分があります:内部 ivar を公開せずに内部状態を反映および変更する KVC プロキシを返すことにより、パブリック プロパティを実装しています。上で定義した public アクセサーのみを使用して、内部配列を変更します。

- (NSMutableArray *)publicBars
{
    return [self mutableArrayValueForKey:@"bars"];
}

@end

プロキシを使用して内部コレクションを変更できます。

Foo *foo = [[Foo alloc] init];

NSMutableArray *bars = foo.publicBars;

[bars addObject:@1];
[bars addObject:@2];
[bars removeObjectAtIndex:0];

 // Now the internal _bars array is @[ @2 ].

Cocoa には、これと同様のパターンの実例があります。たとえば-[NSTextView textStorage]、内部バッキング ストア (から派生したオブジェクトNSMutableAttributedString) を返すものがあります。コレクション オブジェクトではありませんが、これはホスト オブジェクトであるテキスト ビューにミューテーションを渡すミュータブル オブジェクトです。

于 2013-02-11T15:12:34.113 に答える
1

可変プロパティの問題は、他のクラスが所有者に気付かずにコレクションの内容を変更できることです。優れた設計では、特定のタスクを担当するクラスは1つだけであり、この場合はゲームデータを保存します。

私がすることは、可変ディクショナリをstrongプライベートプロパティにし、次の形式でアクセサメソッドを外部に提供することです。

- (void)storeObject:(id)object forSetting:(NSString *)settingName;
- (id)objectForSetting:(NSString *)settingName;

ディクショナリ全体を設定できるようにするために外部クラスが本当に必要な場合は、これが本当に必要であることを確認した後でも、設定すると、の新しいインスタンスを作成する不変のプロパティにすることができます。不変辞書の項目からの内部可変辞書。しかし、私はあなたがその必要性を回避するように設計することができるはずだと思います。

于 2013-02-11T15:07:21.420 に答える
1

私見、変更可能なプロパティを非常にうまく使用できます-Objective-Cのプロパティはカプセル化だけでなく、メモリ管理(ARCによる)にも使用されるためです。

ただし、このプロパティを実装ファイルに保持し、変更不可の読み取り専用として他のクラスに公開する必要があります。

//your_header_file.h

@interface YouClass : NSObject

@property (readonly) NSDictionary *gameState;
@end

//your_implementation_file.m

@interface YouClass ()
@property (nonatomic, strong) NSMutableDictionary *gameState;
@end

@implementation SomeClass

//deal with mutable state here


@end

編集

また、変更可能なコレクションは通常スレッドセーフではないため、常に同じスレッドから変更するようにしてください。

于 2013-02-11T15:04:49.887 に答える