web-dev-qa-db-ja.com

Objective-Cの読み取り専用プロパティ?

インターフェースで読み取り専用プロパティを宣言しました:

 @property (readonly, nonatomic, copy) NSString* eventDomain;

プロパティを誤解しているかもしれませんが、readonlyとして宣言すると、生成されたセッターを実装内で使用できると思いました(.m)ファイルですが、外部エンティティは値を変更できません。 This SO question は、それが起こるべきことだと言っています。それが私が求めている動作です。ただし、標準のセッターまたはドット構文を使用してeventDomain initメソッド内で、unrecognized selector sent to instance.エラー。もちろん私は@synthesizeingプロパティ。このように使用しようとしています:

 // inside one of my init methods
 [self setEventDomain:@"someString"]; // unrecognized selector sent to instance error

プロパティのreadonly宣言を誤解していますか?それとも他に何かが起こっていますか?

87
Alex

セッターも必要であることをコンパイラーに伝える必要があります。一般的な方法は、.mファイルの クラス拡張子 に配置することです。

@interface YourClass ()

@property (nonatomic, copy) NSString* eventDomain;

@end
109
Eiko

読み取り専用プロパティで作業することがわかった別の方法は、@ synthesizeを使用してバッキングストアを指定することです。例えば

@interface MyClass

@property (readonly) int whatever;

@end

その後、実装で

@implementation MyClass

@synthesize whatever = _whatever;

@end

メソッドは、_whateverを設定できます。これは、メンバー変数であるためです。


過去数日間で私が気づいた別の興味深いことは、そのようなサブクラスによって書き込み可能な読み取り専用プロパティを作成できることです。

(ヘッダーファイル内)

@interface MyClass
{
    @protected
    int _propertyBackingStore;
}

@property (readonly) int myProperty;

@end

次に、実装で

@synthesize myProperty = _propertyBackingStore;

ヘッダーファイルの宣言を使用するため、サブクラスはプロパティの値を更新できますが、読み取り専用のままです。

ただし、データの隠蔽とカプセル化に関しては少し残念です。

35
yano

栄子と他の人は正しい答えを与えました。

より簡単な方法は次のとおりです:プライベートメンバー変数に直接アクセスします。

ヘッダー.hファイル内:

@property (strong, nonatomic, readonly) NSString* foo;

実装.mファイルで:

// inside one of my init methods
self->_foo = @"someString"; // Notice the underscore prefix of var name.

それだけです、それだけです。騒ぎも騒ぎもありません。

詳細

Xcode 4.4およびLLVM Compiler 4.0( Xcode 4.4の新機能 )の時点で、他の回答で説明した雑用をいじる必要はありません。

  • synthesizeキーワード
  • 変数を宣言する
  • 実装.mファイルでプロパティを再宣言します。

プロパティfooを宣言した後、Xcodeがアンダースコアの接頭辞_fooで名前が付けられたプライベートメンバー変数を追加したと想定できます。

プロパティがreadwriteと宣言された場合、Xcodeはfooという名前のgetterメソッドとsetFooという名前のsetterを生成します。これらのメソッドは、ドット表記(my Object.myMethod)を使用すると暗黙的に呼び出されます。プロパティがreadonlyと宣言された場合、セッターは生成されません。つまり、アンダースコアで名前が付けられたバッキング変数は、not自体が読み取り専用です。 readonlyは、単にセッターメソッドが合成されなかったことを意味するため、ドット表記を使用して値を設定すると、コンパイラエラーが発生して失敗します。コンパイラは、存在しないメソッド(セッター)の呼び出しを停止するため、ドット表記は失敗します。

これを回避する最も簡単な方法は、アンダースコアで名前が付けられたメンバー変数に直接アクセスすることです。その下線付きの変数を宣言しなくても、そうすることができます! Xcodeはその宣言をビルド/コンパイルプロセスの一部として挿入するため、コンパイルされたコードには実際に変数宣言が含まれます。ただし、元のソースコードファイルにその宣言は表示されません。魔法ではなく、ただ 構文糖

self->を使用すると、オブジェクト/インスタンスのメンバー変数にアクセスできます。これを省略して、変数名を使用することもできます。しかし、self + arrowを使用する方が好きです。コードが自己文書化されるからです。 self->_fooが表示されるとき、_fooがこのインスタンスのメンバー変数であることを明確に理解できます。


ところで、直接的なivarアクセス​​に対するプロパティアクセサーの長所と短所の議論は、Dr。 Matt Neuberg 's iOSのプログラミング 本。私はそれを読んで再読することは非常に有用だと感じました。

34
Basil Bourque

IOSドキュメントの Customizing Existing Classes を参照してください。

readonlyプロパティが読み取り専用であることを示します。 readonlyを指定する場合、@ implementationにはgetterメソッドのみが必要です。実装ブロックで@synthesizeを使用すると、getterメソッドのみが合成されます。さらに、ドット構文を使用して値を割り当てようとすると、コンパイラエラーが発生します。

読み取り専用プロパティにはゲッターメソッドのみがあります。プロパティのクラス内で直接、またはキー値コーディングを使用して、バッキングivarを直接設定できます。

20
Jonah

あなたは他の質問を誤解しています。 その質問で クラス拡張があり、こう宣言されています:

@interface MYShapeEditorDocument ()
@property (readwrite, copy) NSArray *shapesInOrderBackToFront;
@end

それがクラスの実装内でのみ見えるセッターを生成するものです。 Eikoが言うように、クラス拡張を宣言し、プロパティ宣言をオーバーライドして、クラス内でのみセッターを生成するようコンパイラーに指示する必要があります。

7
BoltClock

最短の解決策は次のとおりです。

MyClass.h

@interface MyClass {

  int myProperty;

}

@property (readonly) int myProperty;

@end

MyClass.h

@implementation MyClass

@synthesize myProperty;

@end
5
user2159978

プロパティが読み取り専用として定義されている場合、クラスの内部または他のクラスの外部で使用できるセッターが事実上存在しないことを意味します。 (つまり、それが理にかなっている場合にのみ、「getter」があります。)

その音から、プライベートとしてマークされている通常の読み取り/書き込みプロパティが必要です。これは、インターフェイスファイルでクラス変数をプライベートとして設定することで実現できます。

@private
    NSString* eventDomain;
}
2
John Parker