iOS で合成プロパティの名前を先頭にアンダースコアで変更するのはなぜですか? [重複] 質問する

iOS で合成プロパティの名前を先頭にアンダースコアで変更するのはなぜですか? [重複] 質問する

重複の可能性あり:
Cocoa Objective-C クラスの変数の前にあるアンダースコアはどのように機能しますか?

Xcode 4 で新しいプロジェクトを作成すると、定型コードは実装ファイル内の ivars を合成するときに、次のようにアンダースコア文字を追加します。

@synthesize window = _window;

または:

@synthesize managedObjectContext = __managedObjectContext;

ここで何が達成されているのか誰か教えてもらえますか? 私は完全な素人ではありませんが、これは私が理解できない Objective-C の側面の 1 つです。

もう一つの混乱点は、アプリのデリゲート実装で、上記のようにウィンドウ iVar を合成した後、アプリケーションの didFinishLaunchingWithOptions: メソッドで、ウィンドウと viewController iVar が self を使用して参照されることです。

self.window.rootViewController = self.viewController
[self.window makeKeyAndVisible];

しかし、deallocメソッドでは_windowまたは_viewControllerです

ありがとう

ベストアンサー1

これは、Objective-C ランタイムの以前のバージョンのアーティファクトです。

元々は@synthesizeアクセサー メソッドを作成するために使用されていましたが、ランタイムではインスタンス変数を明示的にインスタンス化する必要がありました。

@interface Foo : Bar {
  Baz *_qux;
}

@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux = _qux;

- (void)dealloc {
  [_qux release];
  [super dealloc];
}

@end

人々は、インスタンス変数をプロパティと区別するために、その前に接頭辞を付けます (Apple はアンダースコアの使用を禁止していますが、これは別の問題です)。プロパティを合成して、インスタンス変数を指します。ただし、重要なのは、 が_quxインスタンス変数であり、self.qux(または) がオブジェクトに送信される[self qux]メッセージであるということです。quxself

ではインスタンス変数を直接使用します-dealloc。代わりにアクセサ メソッドを使用すると、次のようになります (ただし、後ほど説明する理由により、これはお勧めしません)。

- (void)dealloc {
  self.qux = nil; // [self setQux:nil];
  [super dealloc];
}

これには、 を解放する効果がありqux、参照をゼロにします。しかし、これには残念な副作用があります。

  • 予期しない通知が発行される可能性があります。他のオブジェクトが への変更を監視している可能性がありqux、アクセサ メソッドを使用して が変更されると、その変更が記録されます。
  • (この点については誰もが同意するわけではありませんが:) アクセサのようにポインタをゼロにすると、プログラムの論理エラーが隠れてしまう可能性があります。オブジェクトのインスタンス変数にアクセスする場合、オブジェクトが解放された場合、重大な間違いを犯しています。nilただし、Objective-C の -messages セマンティクスのため、アクセサを使用して に設定しても、それを知ることはできませんnil。インスタンス変数を直接解放し、参照をゼロにしなかった場合、解放されたオブジェクトにアクセスすると、大きな が発生しますEXC_BAD_ACCESS

ランタイムのそれ以降のバージョンでは、アクセサ メソッドに加えてインスタンス変数を合成する機能が追加されました。これらのバージョンのランタイムでは、上記のコードはインスタンス変数を省略して記述できます。

@interface Foo : Bar
@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux = _qux;

- (void)dealloc {
  [_qux release];
  [super dealloc];
}

@end

これは実際には、 getter および setter メッセージと によってアクセスされる、とFoo呼ばれるのインスタンス変数を合成します。_qux-qux-setQux:

これはお勧めしません。少し面倒ですが、アンダースコアを使用する理由が 1 つあります。つまり、誤って ivar に直接アクセスすることを防ぐためです。生のインスタンス変数を使用しているのか、アクセサ メソッドを使用しているのかを覚えていると確信できる場合は、代わりに次のようにします。

@interface Foo : Bar
@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux;

- (void)dealloc {
  [qux release];
  [super dealloc];
}

@end

そして、インスタンス変数に直接アクセスしたい場合は、qux(これは、self->quxポインタからメンバーにアクセスするための C 構文では に変換されます) と記述します。アクセサ メソッド (オブザーバに通知し、他の興味深い処理を実行し、メモリ管理に関してより安全で簡単なもの) を使用する場合は、self.qux( [self qux]) とself.qux = blah;( [self setQux:blah]) を使用します。

ここで残念なのは、Apple のサンプル コードとテンプレート コードがひどいということです。これを適切な Objective-C スタイルのガイドとして使用しないでください。また、適切なソフトウェア アーキテクチャのガイドとして使用しないでください。:)

おすすめ記事