@synthesizeを明示的に使用する必要があるのはいつですか?


81

私の知る限り、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コードに明示的に追加する必要があるのはいつですか?


1
サンプルコードが古い可能性があります。基本的に、問題が発生しない限り使用します(たとえば、デリゲートのプロパティは自動合成されません)
borrrden 2013年

1
コメントアウトしてみてください@sythesize。それでもコードが機能する場合は、その必要はありません。
ThomasW 2013年

回答:


171

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

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

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

    @synthesize propertyName = _propertyName
    

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

    • カスタムゲッターとセッターを使用した読み取り/書き込みプロパティ

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

    • カスタムゲッターを使用した読み取り専用プロパティ

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

    • @動的

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

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

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

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

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

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

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

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

最後の3つのケースを除いて、一般的な哲学は、プロパティに関するすべての情報を手動で指定するときはいつでも(すべてのアクセサーメソッドを実装するか、を使用して) @dynamic)、コンパイラはプロパティを完全に制御する必要があると想定し、での自動合成を無効にすることです。それ。

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


元の質問者がまだここにいる場合は、この回答を受け入れる必要があると思います。それは最も完全です。
スティーブンフィッシャー

3
私が覚えている限り、カテゴリで指定されたプロパティも自動的に合成されません。(根本的な理由は、カテゴリにインスタンス変数を追加できないことです。)
Martin R

@MartinR、良い点。これらは自動的に合成さ@synthesizeれませんが、カテゴリで禁止されているため、手動で合成することもできません(すでに述べたように、カテゴリにivarはありません)。メモを追加します。
Gabriele Petronella 2013年

しかし、この答えは特定の質問に対処していません:いつ@synthesizeを使用する必要がありますか?常に、決して、特定の条件が満たされた場合にのみ?
ジェフ

21

明示的に使用しない場合@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?」だと思います。これに対して「決して」以外の回答はありません!


2
同意しました。もう使う@synthesize理由がないので、使用をやめました。また、先頭の下線は、プロパティが提供するメモリ管理とスレッド化セーフティネット(スキップしinitdeallocメソッドとメソッド)を回避していることを知らせるための優れた視覚的フラグとして機能します。
BergQuester 2013年

1
問題は、いつ明示的に合成する必要があるのか​​ということでした。あなたはそれに対して全く答えていません。
フォグマイスター2013年

1
私は今日、これが完全に真実ではないことを発見しました。@synthesizeがない場合は、バッキングインスタンス変数を作成する必要があります。これを使用すると、コンパイラが自動的に実行します。
スティーブンフィッシャー

1
そして、私はこの発見に誰も反対票を投じませんでした。:)
スティーブンフィッシャー

1
-1明示的な合成が必要な場合を完全に見逃しています。また、これはコンパイラ機能であり、Xcode機能ではありません。
Gabriele Petronella 2013年

14

@synthesizeコードに明示的に追加する必要があるのはいつですか?

一般的に、それが必要な場合:あなたはおそらくそれが必要とされるケースにぶつかることは決してないでしょう。

ただし、便利な場合が1つあります。

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

このことを考慮:

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

@implementation MyObject

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

@end

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

ここに画像の説明を入力してください

それを存在させるには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

言い換えると、合成は実用的な目的では決して必要ではありません*が、ゲッター/セッターを提供するときにプロパティバッキングインスタンス変数を定義するために使用できます。ここで使用するフォームを決定できます。

以前は、でインスタンス変数を指定することを好みましたが、冗長な型を削除し、バッキング変数をプロパティに明示的に関連付けるため@implementation {}@synthesizeルートの方が適していると思います。

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

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


本気ですか?やってみましたか?私は多くのカスタムセッターとゲッターを作成しましたが、プロパティを合成したことはありません。(そして私はあなたがうまくいかないと言ったあなたの最初の例を使用しました)
マーク

はい、確かです。コードは新しいプロジェクトからコピーして貼り付けられました。プロパティで非アトミックを指定しない限り、Xcodeの最近のバージョンでは機能しません。
スティーブンフィッシャー

1
はい、それは本当です..しかし、99%の時間、あなたのプロパティは非アトミックです。したがって、プロパティがアトミックであり、実際に合成する必要があるカスタムゲッター/セッターが必要な場合に限ります。
マーク

セッターとゲッターの両方にカスタム実装を提供する場合、コンパイラーは、プロパティを制御していると想定し、それを合成しません。
Gabriele Petronella 2013年

2
私はそこであなたに同意しますが、Appleが最初にそれが良い考えだと思った理由は理解できます。本当の苦痛は、atomic後でまでプロパティ指定子として追加されなかったことです。そこにあるので、警告フラグがCLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES面白いと思うかもしれません。
スティーブンフィッシャー

7

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

@property NSString *name;

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

@synthesize name = _name;

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

self.name;
// or
_name;

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

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

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

ただし、経験則として。

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


1
自動合成が行われない場合が多くあります。私の答えをチェックしてください。
Gabriele Petronella 2013年

@GabrielePetronella平均的な読者のために、これらのケースを簡潔にリストできますか?
ダンローゼンスターク

@DanRosenstarkこれは、現在プログラミングしている人には関係ありません。あなたは本当にSwiftを使うべきです。そしてTBH私は合成全体がObjCのものではなくなったと思います。5年以上前のコードベースで作業している場合を除きます。
フォグマイスター

@Fogmeisterはい、それはあなたがあなたの答えで言及する場合(あなたがセッターとゲッターの両方をオーバーライドする場合)でも問題です。そして、Objective-Cが「今プログラミングしている人とは関係がない」ということについては、私の日常の仕事に電話して伝えてください。また、Objective-Cを社内で使用しているFacebook、Google、Appleに大きな効果をもたらします。
ダンローゼンスターク

:私は確かにこれはQuoraのなしに真のですが、とにかくだ quora.com/...
ダンRosenstark

1

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


本当ですが、それはたった1つのケースです。もっと詳しく説明したいと思うかもしれません。
Gabriele Petronella 2013年

@GabrielePetronellaこの遅い時間に私が考えることができるのはそれだけです。=]
Leo Natan 2013年

0

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

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

だから今、それらをコメントアウトしたので、私は通過し、それぞれの出来事を例えばに置き換えました

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


-1

Xcodeは明示的なものを必要としません @synthesize宣言を。

あなたがそれを@synthesizeするのと同じように書かないなら:

@synthesize manager = _manager;

サンプルコードは古い可能性があります。彼らはすぐにそれを更新します。

次のようなプロパティにアクセスできます。

[self.manager function];

これはAppleが推奨する規則です。私はそれに従います、そして私はあなたもそうすることをお勧めします!


2
ステートメント[_manager function]はプロパティにアクセスせず、代わりに基になるivarに直接アクセスします。
CouchDeveloper 2013年

@CouchDeveloperニッチピッキングする場合は、正確にする必要があります。プロパティは、ivarを含むいくつかのもので構成されています。したがって、プロパティのアクセサ使用[_manager function]しないと言った方がよいでしょう。
Nikolai Ruhe 2013年

@ CouchDeveloper-ありがとうございます!修正しました!:)
サムフィッシャー

1
@NikolaiRuheあなたは正しいニコライです、私はもっと正確にすべきでした。;)
CouchDeveloper 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.