1

ARCを使用しています。

これは私の .h ファイルです

...
- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t;

@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
...

これは私の.mファイルです

....
@synthesize coordinate, title;

- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
    self = [super init];
    if (self) {
        coordinate = c;
        [self setTitle:t];
     }
    return self;
}
....
  1. このように座標を設定するのは正しい方法ですか? として宣言していることを考えるとreadonly、それが唯一の方法のようです。デフォルト (つまりreadwrite) をそのまま使用するとどうなりますか? この場合、代わりにセッター メソッド [self setCoordinate] を使用する必要がありますか?

  2. タイトルtitle = tも同様に設定できました。setter メソッドを使用する場合と比較すると、結果は同じですが、違いは何ですか?


ありがとう!すべての回答を受け入れることができれば幸いです。

4

5 に答える 5

3

実際には、常に初期化メソッドで ivar を直接設定することになっています。readonlyこれは、 orプロパティがあるかどうかに関係なく当てはまりreadwriteます。ここのドキュメントにもそう書かれています。

この背後にある理由は、継承に関係しています。誰かがあなたのクラスをサブクラス化し、あなたが作成した ivar をバイパスするようにプロパティのセッターを上書きした場合 (または他の奇妙なことをした場合)、突然、初期化メソッドの元の実装は、書かれていることを実行しなくなります。 . 特に、アクセサーをオーバーライドするサブクラスが原因で、初期化子が奇妙な状態のオブジェクトを作成してしまう可能性があります。ARC以前の時代には、この種のことが起こると、トリッキーな(または単に壊れた)メモリ状況になる可能性もありました. 重要なメッセージは次のとおりです。既知の有効な状態を持つオブジェクトを常に作成するように、初期化子を作成する必要があります。

したがって(ARCを使用していると仮定すると)、イニシャライザは実際には次のようになります。

- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
    self = [super init];
    if (self) {
        coordinate = c;
        title = [t copy];
     }
    return self;
}

個人的には、いつプロパティを使用しているのか、いつ ivar に直接アクセスしているのかを明確にするために、最初のアンダースコアで ivar を合成することを好みます (LLVM 4.0 では、自動的に合成されたプロパティに対してもこれを行うようになりました)。

@synthesize coordinate = _coordinate;
@synthesize title = _title;

- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
    self = [super init];
    if (self) {
        _coordinate = c;
        _title = [t copy];
     }
    return self;
}
于 2012-10-28T03:34:58.133 に答える
2

1: あなたのコードが今のように、はい、それが正しい方法です。ARC を使用していない場合 (現在使用していると仮定) は、所有権を主張するために値を保持する必要もあります。これは、ARC の下で自動的に行われます。それが唯一の方法ではないことに注意してください。readwrite実装ファイルのクラス拡張のようにプロパティを再宣言できます。これは、プロパティをクラスのユーザーに提供しreadwriteながら、プロパティの利点を得ることができる一般的な方法です。readonly元。

//MyClass.h

@interface MyClass : NSObject
@property (nonatomic, strong, readonly) NSNumber* number;
- (void) initWithNumber:(NSNumber*)number;
@end

//MyClass.m

@interface MyClass ()
@property (nonatomic, strong, readwrite) NSNumber* number;
@end

@implementation MyClass
//this changes the instance variable backing the property to _number.
@synthesize number = _number;

- (void) initWithNumber:(NSNumber*)number{
    self = [super init];
    if (self) {
        self.number = number;
    }
    return self;
}
@end

結局のところ、KVO に準拠し、値がいつ変更されたかを常に把握できるように、できる限りセッターを使用するのは良い習慣だと思います。たとえば、外観に反映されるプロパティを持つカスタム UIView がある場合、変更されたときに自分自身を再表示する可能性があります。setNeedsDisplayこれを行う最も簡単な方法は、セッターを自分で実装し、値を設定した後に呼び出すことです。プロパティを直接サポートするインスタンス値を設定した場合、それはできませんでした。setneedsDisplayクラスのユーザーは、設定するたびに手動で呼び出すことを覚えておく必要があります。

2: 1 つは setter メソッドを通過し、いつ値が設定されるかを知る方法を提供します。もう 1 つは、プロパティをサポートするインスタンス変数に値を設定します。setter メソッドは常に指定された方法でメモリ管理を処理しますが、一貫したスキームを維持するために、インスタンス変数に直接代入する場合に settercopyの値を ing するなどの操作はユーザー次第です。copy注意しないと、時々セッターを通過し、他のセッターを通過しないと、厄介なバグが発生する可能性があります。セッターを経由しないと、値がいつ変更されるかを知るのが難しくなり、無効な値を除外することがほぼ不可能になります。たとえば、intある範囲内の値に制限したいプロパティがあり、誰かが最小制限未満の値を渡した場合、おそらくプロパティを範囲内の可能な限り低い値に設定したいと思うでしょう。値が最初にセッターを通過しないと、それを行うことはできません。

于 2012-10-28T03:06:32.010 に答える
1

はい、そのように設定しても問題ありません。プロパティを常に使用したい場合は、クラス拡張でプロパティを読み取り専用ではなく読み取り/書き込みにオーバーライドできます。Foo.m で:

@interface Foo ()
@property (nonatomic) CLLocationCoordinate2D coordinate;
@end

@implementation Foo {
    // ...
    self.coordinate = c;
}
于 2012-10-27T21:08:22.790 に答える
1
  1. そのように座標を設定することは正しく、プロパティを宣言した場合はそれを行う唯一の方法ですreadonly

  2. を使用してタイトルを設定することは、 を使用してタイトルをtitle = t設定することとは異なり[self setTitle:t]ます。インスタンス変数に直接割り当てると、NSString引数として渡されたインスタンスがそのまま保持されますt。ただし、アクセサー メソッドを使用する場合、アクセサーは文字列に自分自身をコピーするように要求します ( property を宣言したためcopy)。引数として与えられた文字列tが実際に であるNSMutableString場合、その不変のコピーを取得します。引数として与えられた文字列tがすでに不変の文字列である場合、コピーを求められたときにそれ自体が返されます。

于 2012-10-27T22:06:58.823 に答える
0
self.coordinate = c;

基本的に、呼び出しと同じになるようにコンパイルされます

[self setCoordinate:c];

coordinate = cとの違い[self setCoordinate:c];は、1 つ目は変数を直接設定するだけで、2 つ目はメソッドを呼び出すことです。

注意すべき理由は、実装がどのように書かれているかによって、メソッドが潜在的に副作用を持つ可能性があることです(例:愚かな例)

- (void)setCoordinate:(CLLocationCoordinate2D)coordinate;
{
    _coordinate = coordinate;
    [self doSomethingCrazy];
}
于 2012-10-27T22:00:22.103 に答える