1. ホーム
  2. objective-c

[解決済み] iOSの合成されたプロパティの名前を、先頭のアンダースコアで変更するのはなぜですか?重複

2022-07-31 07:46:20

質問

重複の可能性があります。

cocoa objective-cクラスの変数の前にあるアンダースコアはどのように動作しますか?

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

@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です。

ありがとうございます。

どのように解決するのですか?

これは、以前のバージョンの 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 への変更を観測している可能性があり、アクセッサメソッドで変更されると記録されます。
  • (この点については誰もが同意しているわけではありません:) アクセッサが行うポインタのゼロ化は、プログラムのロジック エラーを隠す可能性があります。もしあなたが、あるオブジェクトのインスタンス変数にアクセスすることがあれば の後に の後に、オブジェクトが解放された場合、何か重大な間違いを犯していることになります。なぜなら、Objective-Cの nil -メッセージングのセマンティックスのため、あなたは決して知ることができませんが、アクセサを使って nil . もし、インスタンス変数を直接解放し、参照をゼロにしなかったら、解放されたオブジェクトにアクセスすることで、大音量の EXC_BAD_ACCESS .

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

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

@implementation Foo
@synthesize qux = _qux;

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

@end

これは実際にインスタンス変数を Foo という _qux と呼ばれ、ゲッターとセッターのメッセージによってアクセスされます。 -qux-setQux: .

これは少し面倒ですが、アンダースコアを使用する良い理由があります。もしあなたが、生のインスタンス変数とアクセッサメソッドのどちらを使用しているか覚えていると自分自身を信じることができると思うなら、代わりにこのようにすればいいのです。

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

@implementation Foo
@synthesize qux;

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

@end

そして、インスタンス変数に直接アクセスしたい場合は、次のように記述します。 qux (とすればよいのです(訳すと self->qux に変換されます)。アクセサメソッド(オブザーバに通知したり、他の面白いことをしたり、 メモリ管理に関してより安全で簡単にする)を使いたいときは self.qux ( [self qux] ) と self.qux = blah; ( [self setQux:blah] ).

ここで悲しいのは、Appleのサンプルコードとテンプレートコードは最悪だということです。適切な Objective-C スタイルのガイドとしては決して使用せず、適切なソフトウェア アーキテクチャのガイドとしても絶対に使用しないでください。)