web-dev-qa-db-ja.com

iOSの合成プロパティの名前をアンダースコアで変更するのはなぜですか?

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

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

@synthesize window = _window;

または:

@synthesize managedObjectContext = __managedObjectContext;

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

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

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

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

ありがとう

126
Alpinista

これは、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])はオブジェクトquxに送信されるメッセージselfです。

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

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

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

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

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

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

@implementation Foo
@synthesize qux = _qux;

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

@end

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

これに反対することをお勧めします。それは少し厄介ですが、アンダースコアを使用する理由が1つあります。つまり、偶発的な直接ivarアクセス​​から保護するためです。生のインスタンス変数を使用するのか、アクセサメソッドを使用するのかを覚えていると自分が信頼できると思う場合は、代わりに次のようにしてください:

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

@implementation Foo
@synthesize qux;

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

@end

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

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

223

別の理由があります。インスタンス変数に下線を付けないと、パラメーターself.title = titleおよびself.rating = rating

@implementation ScaryBugData
@synthesize title;
@synthesize rating;
- (id)initWithTitle:(NSString *)title rating:(float)rating {
    if (self = [super init]) {
        self.title = title; // Warning. Local declaration hides instance variable
        self.rating = rating; // Warning. Local declaration hides instance variable
    }
    return self;
}
@end

インスタンス変数に下線を引くことにより、警告を回避します。

@implementation ScaryBugData
    @synthesize title = _title;
    @synthesize rating = _rating;
    - (id)initWithTitle:(NSString *)title rating:(float)rating {
        if (self = [super init]) {
            self.title = title; // No warning
            self.rating = rating; // No warning
        }
        return self;
    }
    @end
13
Freeman

アプリケーションのdidFinishLaunchingWithOptions:メソッドでは、windowおよびviewController ivarsはselfを使用して参照されます

いいえ、そうではないです。これらはpropertieswindowおよびviewControllerへの参照です。これがアンダースコアのポイントです。プロパティが使用されているとき(アンダースコアなし)、およびivarが直接アクセスされているとき(アンダースコアを使用)を明確にするためです。

6
LaC

はい、オブジェクトの参照を区別するだけです。つまり、オブジェクトが直接参照される場合はアンダースコアを使用し、そうでない場合はselfを使用してオブジェクトを参照します。

2
Krishna Kishore