web-dev-qa-db-ja.com

@synthesizeを明示的に使用する場合

私の知る限り、XCode 4.4以降は@synthesizeは、プロパティアクセサーを自動生成します。しかし、ちょうど今、私はNSUndoManagerに関するコードのサンプルを読みましたが、コードでは@synthesizeは明示的に追加されます。好む:

@interface RootViewController  ()

@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@property (nonatomic, strong) NSUndoManager *undoManager;

@end

@implementation RootViewController
//Must explicitly synthesize this
@synthesize undoManager;

今戸惑っています...いつ追加する必要がありますか@synthesize私のコードに明示的に?

68
罗泽轩

たくさんの答えがありますが、大きな混乱もあります。私はいくつかの注文をしようとします(または混乱を増やす、私たちが見るでしょう...)

  1. Xcodeの話をやめましょう。 Xcodeは[〜#〜] ide [〜#〜]です。 clangはcompilerです。私たちが議論しているこの機能は、プロパティの自動合成と呼ばれ、それは clangでサポートされているObjective-C言語拡張機能 です。これはXcodeで使用されるデフォルトのコンパイラです。
    明確にするために、Xcodeでgccに切り替えると、(Xcodeバージョンに関係なく)この機能のメリットは得られません。同様に、テキストエディターを使用し、clangを使用してコンパイルする場合コマンドライン、あなたがします。

  2. 自動合成のおかげで、プロパティはコンパイラによって自動的に合成されるため、明示的にプロパティを合成する必要はありません。

    @synthesize propertyName = _propertyName
    

    ただし、いくつかの例外があります。

    • カスタムgetterおよびsetterを使用したreadwriteプロパティ

      both両方のゲッターとセッターのカスタム実装を提供する場合、プロパティは自動的に合成されません

    • カスタムゲッター付きの読み取り専用プロパティ

      読み取り専用プロパティにカスタムゲッター実装を提供する場合、これは自動的に合成されません

    • @ dynamic

      @dynamic propertyNameを使用する場合、プロパティは自動的に合成されません(@dynamic@synthesizeは相互に排他的であるため、かなり明白です)

    • @ protocolで宣言されたプロパティ

      プロトコルに準拠する場合、プロトコルが定義するプロパティは自動的に合成されません。

    • カテゴリで宣言されたプロパティ

      これは、@synthesizeディレクティブがコンパイラによって自動的に挿入されない場合ですが、このプロパティも手動で合成することはできません。カテゴリはプロパティを宣言できますが、カテゴリはivarを作成できないため、プロパティをまったく合成できません。完全を期すために、まだ可能だと付け加えます Objective-Cランタイムを使用してプロパティ合成を偽造するため

    • オーバーライドされたプロパティ(clang-600.0.51以降の新機能、Xcode 6に付属、MarcSchlüpmannに感謝)

      スーパークラスのプロパティをオーバーライドする場合、明示的に合成する必要があります

プロパティを合成すると、バッキングivarが自動的に合成されるため、プロパティの合成が欠落している場合、明示的に宣言されていない限り、ivarも欠落することに注意してください。

最後の3つのケースを除いて、一般的な哲学は、プロパティに関するすべての情報を手動で指定するときはいつでも(すべてのアクセサメソッドを実装するか、@dynamicを使用して)、プロパティと自動合成を無効にします。

上記の場合とは別に、明示的な@synthesizeを使用する他の唯一の方法は、異なるivar名を指定することです。ただし、規則は重要であるため、私のアドバイスは常にデフォルトの命名を使用することです。

155

明示的に@synthesizeを使用しない場合、コンパイラは、記述した場合と同じ方法でプロパティを理解します

@synthesize undoManager=_undoManager;

次のようなコードを記述できるようになります。

[_undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

これは一般的な規則です。

書いたら

@synthesize undoManager;

あなたが持っています:

[undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

個人的には@synthesizeの使用をやめました。これはもう必須ではないからです。私にとって@synthesizeを使用する唯一の理由は、iVar@propertyにリンクすることです。特定のゲッターとセッターを生成する場合。しかし、指定されたコードにはiVarがありません。この@synthesizeは役に立たないと思います。しかし、今では新しい質問は「いつiVarを使用するか?」であり、この質問に対して「決して」以外の応答はありません。

17
KIDdAe

いつ@synthesizeをコードに明示的に追加すべきですか?

通常、必要な場合:必要な場合はおそらくヒットしません。

ただし、役に立つ場合が1つあります。

カスタムgetterとsetterの両方を書いているが、それを裏付けるインスタンス変数が必要だとします。 (アトミックプロパティの場合、これはカスタムセッターを必要とするのと同じくらい簡単です。アトミックプロパティではなく単原子プロパティのセッターを指定すると、コンパイラはゲッターを書き込みます。)

このことを考慮:

@interface MyObject:NSObject
@property (copy) NSString *title;
@end

@implementation MyObject

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

_titleが存在しないため、これは機能しません。ゲッターまたはセッターの両方を指定しているため、Xcodeは(正しく)バッキングインスタンス変数を作成しません。

enter image description here

存在させるには2つの選択肢があります。 @implementationを次のように変更できます。

@implementation MyObject {
    NSString *_title;
}

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

または、これに変更します:

@implementation MyObject

@synthesize title = _title;

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

言い換えると、合成は実用的な目的のために決して必要ではありません*が、ゲッター/セッターを提供しているときにdefine property-backingインスタンス変数に使用できます。ここで使用するフォームを決定できます。

以前は、@implementation {}でインスタンス変数を指定することを好みましたが、冗長な型を削除し、バッキング変数をプロパティに明示的に結び付けるので、@synthesizeルートがより良い選択だと思います:

  1. プロパティのタイプを変更すると、インスタンス変数のタイプが変更されます。
  2. ストレージ修飾子を変更し(たとえば、強いのではなく弱くするか、弱いのではなく強くする)、ストレージ修飾子を変更します。
  3. プロパティを削除するか名前を変更すると、@synthesizeはコンパイラエラーを生成します。迷子のインスタンス変数で終わることはありません。

*-複数のファイルのカテゴリ間で機能を分割することに関連して、必要なケースを1つ知っています。そして、Appleがこれを修正するか、すでに持っていたとしても、驚かないでしょう。

11
Steven Fisher

OK、プロパティを作成するとき...

@property NSString *name;

Xcodeは、あなたが書いたようにiVarを自動合成します...

@synthesize name = _name;

これは、プロパティにアクセスできることを意味します...

self.name;
// or
_name;

どちらでも機能しますが、self.nameは実際にアクセサメソッドを使用します。

自動合成が機能しないのは一度だけです:セッターとゲッターメソッドを上書きする場合、iVarを合成する必要があります。

セッターをオーバーライドするだけでも、ゲッターをオーバーライドするだけでも大丈夫です。しかし、両方を行うと、コンパイラはそれを理解できず、手動で合成する必要があります。

経験則として。

IVarsを作成しないでください。プロパティを使用するだけです。合成しないでください。

6
Fogmeister

プロトコルでプロパティが宣言されている場合、プロパティ合成が必要です。実装インターフェイスでは自動的に合成されません。

1
Leo Natan

それを明確にしてくれてありがとう。同様の問題がありました。

@synthesize firstAsset, secondAsset, audioAsset;
@synthesize activityView;

だから今、それらをコメントアウトして、私は通り抜けて、例えば

self.firstAsset firstAssetを使用することもできますが、 ""が頻繁に表示されないことがあります。

0
Harry McGovern