2

次のような単純なクラスがあるとしましょう。

@interface A { 
// @public
    int var;
}
// @property(some_property) int var;
@end

変数varにアクセスしたい場合、いくつかのオプションがあります。varを公開すると、次のことができます。

A objectA = [ [ A alloc ] init ];
NSLog( @"%d", objectA->var );
objectA->var = someNumber;

代わりにプロパティにする場合は、次のようなことを行う必要があります。

A objectA = [ [ A alloc ] init ];
NSLog( @"%d", objectA.var ); // dot-syntax
NSLog( @"%d", [ objectA var ] ); // get-syntax
[ objectA setVar: someNumber ];

私は両方を試しましたが、それらは正常に機能しますが、私の質問は、オブジェクト内の変数にアクセスするために古いスタイルのポインター表記を使用することはどれほど危険ですか?メソッドへのアクセスを標準化することで、今すぐ対処する必要があることについて、後で心配する必要がありますか?それとも、それが機能する限り、私はそれをやり遂げることができますか?

4

5 に答える 5

12

別の質問に対する私のコメントが数日前に同様の議論を始めたので、私は自分の2セントを投入します。

オブジェクトのクラスの実装外でオブジェクトのプロパティを設定および取得するには、常にアクセサメソッドを使用する必要があります。クラスの実装内であっても、ほとんどの場合、アクセサメソッドを使用してクラスのプロパティにアクセスする必要があります。

いくつかの理由のリストを次に示します。

  • カプセル化クラスは、他のプロパティと同じように外界に見えるプロパティを公開する場合がありますが、内部的には対応するivarによってサポートされていません。おそらく、それは実際には別の内部プロパティの変換を行います。さらに重要なことに、この実装は将来変更される可能性があります。オブジェクト指向コードでカプセル化する主な理由の1つは、クラスの外部ユーザーが変更を加えることなく、クラスの内部実装を変更できることです。(私はちょうど今朝、重要な古いクラスで、ivarから完全に異なるバッキングメソッドにそのような変更を加えました。他のクラスの束、または同じクラスのメソッドでさえ、直接ivarアクセス​​を行っていた場合、私が行ったよりもはるかに多くのコードを変更する必要があったでしょう。)

  • メモリ管理。これはARCではそれほど大きな問題ではありません。どちらの方法でも適切に処理されるためです(ほとんどの場合、以下を参照)が、手動のメモリ管理では、プロパティのアクセサメソッドが基になるオブジェクトを適切に保持および解放します。 。このコードを1つの場所に保持すると、メモリ管理のミス(リークやオーバーリリース)の可能性がはるかに低くなり、読みやすく、変更しやすく、コードの冗長性が低くなります。ARCが有効になっている場合でも、コピー動作を使用して宣言されたプロパティは、コピーを実行するためにsetterメソッドに依存し、直接ivarアクセス​​を行う場合はそれをバイパスします。

  • セットでのカスタム動作。これは実際にはカプセル化の別の部分にすぎません。対応するセッターが呼び出されたときに、ivarを新しい値に設定するだけでなく、クラスが何かをしたいということは非常に一般的です。非常に一般的で単純な例の1つは[self setNeedsDisplay]、外観に影響を与えるプロパティが変更されたときにNSViewサブクラスが呼び出すことです。セッターを呼び出さず、代わりにivarを直接設定すると、この動作を完全にバイパスできます。繰り返しになりますが、セッターがそのようなことをする必要がないことを知っていれば問題ないと思うかもしれませんが、要件は変わります。最初からセッターを使用することで、変更がはるかに簡単になります。

  • get/レイジーインスタンス化のカスタム動作。これの最も一般的な理由の1つは、遅延インスタンス化を行うことです。プロパティのgetterメソッドを実装して、基になるivarがnilであるかどうかを確認し、nilである場合は、最初にivarを初期化してから返します。次回呼び出されると、初期化が二度と起こらないようにivarが設定されます。これは、高価な(CPU集約型および/またはメモリ集約型)オブジェクトの作成を、実際に必要になるまで延期する簡単な方法です。たとえば、起動時間を改善するためのパフォーマンスの最適化として行われることがよくあります。これは、外部コードの既存のクラスの使用を中断することなく、クラスの実装を簡単に改善できるカプセル化の完璧な例です。

  • KVOコンプライアンス。Objective-Cは、Key Value Observingと呼ばれるメカニズムを提供します。これにより、あるオブジェクトが別のオブジェクトの特定のプロパティが変更されたときに通知を要求できるようになります。適切な名前のアクセサーまたは合成された@propertiesを使用ただし、KVOが機能するには、アクセサメソッドを実際に呼び出す必要があります。ivarを直接変更した場合、そのivarの対応するプロパティのオブザーバーには通知されません。別のオブジェクトのプロパティを設定する場合でも、自分自身のプロパティを設定する場合でも、オブザーバーがそのプロパティの変更に登録されているかどうかはわかりません。また、変更の通知をオブザーバーに短絡させています。

  • 読みやすさ。これはあなたの例に正確には当てはまりません。コンパイラによって暗示/挿入される、クラス自体の実装( )でobjectA->varの直接ivarアクセス​​により適しています。問題は、特に長いメソッドでは、割り当てステートメントが表示され、割り当てられている変数が現在のスコープに対してローカルであるか、インスタンス変数であるかが一目でわからない場合があることです。これは、アンダースコアやハンガリアン記法などをivarに付けるなどの命名規則で軽減できますが、Objective-Cの規則は、orを使用するのが最適であることを意味します(ちなみに、これは意味的にまったく同じです)。var = ...self->self.var = ...[self setVar:...]

  • なぜだめですか?アクセサメソッドを使わないものもありますが、その理由は考えられません。変数名の前に「self」を付けるので、入力するのはそれほど速くありません。それほど多くのタイピングは必要ありません。ObjCメッセージ送信を追加するため、アクセサーの呼び出しにはパフォーマンスの低下が伴います。ただし、このペナルティは非常に小さく、もちろん、時期尚早の最適化は悪いことです。アクセサメソッド呼び出しのオーバーヘッドがアプリケーションのパフォーマンスに深刻な影響を与えるのに十分であることは非常にまれです

クラスの実装内での直接ivarアクセス​​の使用に関して、「ほぼ」修飾子を使用したことを思い出してください。(プロパティを@synthesizingするのではなく)カスタムアクセサメソッドを実装する場合は、もちろん、アクセサの実装内のivarに直接アクセスする必要があります。-initまた、同意しない人もいますが、クラスの初期化子( )メソッド内にivarを直接設定することをお勧めします(そしてAppleの推奨)。これは、クラスが完全に初期化されていない間は、セッターの副作用を回避したい場合があるためです。たとえば、通知オブジェクトが一貫性のない/部分的に初期化された状態であることがわかったときに、KVOオブザーバーに変更が通知されないようにする必要があります。

于 2013-02-15T22:31:09.153 に答える
5

古いスタイルのポインタ表記を使用してオブジェクト内の変数にアクセスすることはどれほど危険ですか?

とても危ない。

メソッドへのアクセスを標準化することで、今すぐ対処する必要があることについて、後で心配する必要がありますか?

はい。これらの他の心配事項には、メモリ管理(Memory。Management !!!)、「Key-Value Observing」が機能しないなどがあります。これはすべて->、を使用してインスタンス変数に直接アクセスするために発生します(一部の過度に厳格なOOファンはカプセル化に違反していると言いますが、ドット表記は適切なゲッターメソッドとセッターメソッドを呼び出します(メモリの適切な管理、KVOリスナーへの通知など)。

したがって、全体として、アクセサメソッド(および必要に応じてドット表記)を使用し、インスタンス変数に直接アクセスしないでください。

于 2013-02-15T21:55:40.773 に答える
2

プロパティフォームを使用する必要があります。このアプローチはより標準的であるため、チームでうまく機能します。また、呼び出し元が使用するインターフェイスを変更せずに、後で実装を変更できるという明確な利点もあります。

于 2013-02-15T21:56:10.740 に答える
2

ARCの前は、プロパティタイプ参照(または保持/解放セマンティクスのいずれかを使用するか、保持/解放セマンティクスを監視するのに対し、直接iVar参照( )は使用しないという点で大きな違いがありました。ただし、ARCではかなり混乱しています。[thing setMyVar:value];thing.myVar = value;thing ->myVar = value;

于 2013-02-15T21:58:57.993 に答える
0

はい、古いスタイルのポインタ表記を使用する方が危険です。Objective-Cでは、ドット表記の使用は「setVar」メソッドの使用とまったく同じです。

[objectA setVar:someNumber]; // Exactly the same as objectA.var = someNumber;

私が「まったく同じ」と言うとき、私はそれを意味します。objectA.var = someNumberを使用すると、setterメソッドを呼び出します(インスタンス変数を変更する前に値を変更および検証できます)。ポインタスタイルの割り当てを使用する場合は、直接変更するため、アプリケーションのビジネスロジックに応じて、間違っている可能性のある値を保存できます。

言い換えれば、常にObjective-Cのセッター/ゲッターの規則を使用してください(何らかの理由でそこにあります)。

私は特に、objectA.var = someNumber構文を使用するのが好きです。なぜなら、他の言語のプログラマーにとって理解しやすいからです(Objective-Cのメソッド呼び出しは奇妙で、Objective-Cがセッター/ゲッターを作成することを知らない場合はプロパティを合成するときに自動的に、それらを読み取るのは難しくなります)。

于 2013-05-22T02:09:50.690 に答える