web-dev-qa-db-ja.com

NSMutableArrayに裏打ちされたNSArray @ property

パブリックプロパティをNSArrayでバックアップされているかのように表示するクラスを定義しました。これは非常に簡単ですが、私の場合、実際のバッキングivarはNSMutableArrayです。

@interface Foo
{
    NSMutableArray* array;
}
@property (nonatomic, retain) NSArray* array;

@end

実装ファイル(*.m)でプロパティを@synthesizeしましたが、self.wordsを使用することは、NSArrayを変更しようとすることと同じであるため、すぐに警告が表示されます。

これを行う正しい方法は何ですか?

ありがとう!

39
Matty P

ヘッダーでreadonlyNSArrayを宣言し、その配列のゲッターをオーバーライドして、実装で宣言されたプライベートNSMutableArrayのコピーを返します。次のことを考慮してください。

Foo.h

@interface Foo

@property (nonatomic, retain, readonly) NSArray *array;

@end

Foo.m

@interface Foo ()

@property (nonatomic, retain) NSMutableArray *mutableArray

@end

#pragma mark -

@implementation Foo

@synthesize mutableArray;

- (NSArray *)array
{
    return [[self.mutableArray copy] autorelease];
}

@end
20
Mark Adams

基本的に、NSArrayプロパティをヘッダーファイルのcategoryに配置し、NSMutableArrayプロパティを実装ファイルのclass extensionに配置します。そのようです...

Foo.h:

@interface Foo
@end

@interface Foo (Collections)
@property (nonatomic, readonly, strong) NSArray *someArray;
@end

Foo.m

@interface Foo ()
@property (nonatomic, readwrite, strong) NSMutableArray *someArray;
@end
14
sam

シンプル:

1)プロパティが1つでない場合は、プロパティを使用しないでください。

2)コードは次のように簡略化されます。

- (NSArray *)currentArray {
    return [NSArray arraywithArray:mutableArray]; // need the arrayWithArray -    otherwise the caller could be in for surprise when the supposedly unchanging array changes while he is using it. 
}

- (void)setArray:(NSArray *)array {
    [mutableArray setArray:array];
}

オブジェクトが割り当てられたら配列を作成し、オブジェクトが死んだら配列の割り当てを解除します。

「。」を使用するだけで大​​きな影響が発生する場合。演算子、非常に非効率的なコードを見落としがちです。アクセサーはそれだけです。また、誰かがaFoo.arrayを呼び出した場合、コントラクトはfooの配列メンバーにアクセスすることですが、実際には呼び出し時のコピーにすぎません。違いは十分に現実的であり、ここに投稿された他の含意にバグを引き起こしました。

5
Tom Andersen

更新:この回答は無効になりました。以下の推奨ソリューションのいずれかを使用してください。

最近では、次のことができます。

Foo.m:

@implementation Foo {
    NSMutableArray* _array;
}

@end

Foo.h:

@interface Foo

@property (readonly, strong) NSArray* array;

@end

実装の内部からivarで可変_arrayをアドレス指定でき、実装の外部では不変プロパティを介してアクセスできます。残念ながら、これは他の人がそれをNSMutableArrayにキャストして変更できないことを保証するものではありません。馬鹿からの保護を強化するには、アクセサメソッドを定義し、不変のコピーを返す必要がありますが、場合によっては非常にコストがかかる可能性があります。

読み取り専用データを返す必要がある場合は、単純なアクセサーメソッドを使用する方がよいという上記のコメントの1つに実際に同意します。これは、明らかにあいまいさが少なくなります。

4
Ben Sinclair

これは、プロパティが実際のivarのクラスタイプと一致する必要があるためです。

考えられる解決策/回避策:

//Foo.h:
@interface Foo
{
    NSMutableArray* mutableArray;
}
@property (readwrite, nonatomic, retain) NSArray* array;
//or manual accessor declarations, in case you're picky about wrapper-properties.
@end

//Foo.m:
@interface Foo ()
@property (readwrite, nonatomic, retain) NSMutableArray* mutableArray;
@end

@implementation

@synthesize mutableArray;
@dynamic array;

- (NSArray *)array {
    return [NSArray arrayWithArray:self.mutableArray];
}

- (void)setArray:(NSArray *)array {
    self.mutableArray = [NSMutableArray arrayWithArray:array];
}

@end

クラス拡張にプライベートmutableArrayプロパティを追加し、パブリックarrayをプライベートの可変プロパティに転送するだけです。

ObjCの最新の言語拡張機能を使用すると、

{
    NSMutableArray* mutableArray;
}

可能であれば、ivarは完全にブロックします。

そして、次のように、合成を通じてivarを定義します。

@synthesize mutableArray = _mutableArray;

これにより、NSMutableArray *_mutableArray;インスタンスが生成されます。

2
Regexident

最も簡単な答え:プロパティタイプ(NSArray)がインスタンス変数タイプ(NSMutableArray)と一致しません

これは、独自のバッキング変数を定義するべきではないもう1つの理由です。@ synthesizeにインスタンス変数を設定させます。手でそれをしないでください。

0
cdstamper