66

C と Objective-C の使用法と構文の違いのいくつかに頭を悩ませようとしています。特に、C と Objective-C でドット演算子とアロー演算子の使用法がどのように (そしてなぜ) 異なるのかを知りたいです。簡単な例を次に示します。

C コード:

// declare a pointer to a Fraction
struct Fraction *frac;

...

// reference an 'instance' variable
int n = (*frac).numerator;     // these two expressions
int n = frac->numerator;       // are equivalent

Objective-C コード:

// declare a pointer to a Fraction
Fraction *frac = [[Fraction alloc] init];

...

// reference an instance variable
int n = frac.numerator;        // why isn't this (*frac).numerator or frac->numerator??

では、両方のプログラムでどのようfracに同じであるか (つまり、Fraction オブジェクトまたは構造体へのポインターである) を見て、プロパティにアクセスするときに異なる構文を使用するのはなぜでしょうか? 特に、C では でnumeratorプロパティにアクセスしますfrac->numeratorが、Objective-C では でドット演算子を使用してアクセスしますfrac.numerator。は両方のプログラムでポインターであるためfrac、これらの式が異なるのはなぜですか? 誰でも私のためにこれを明確にするのを助けることができますか?

4

5 に答える 5

87

frac実際には両方のプログラムで同じではありません。

ACFractionは ですstruct。これは、オーバーロードされた演算子を持たない基本型であり、実際にはデフォルトでのみ構築および破棄できます。構造体で関数またはフィールドを定義する場合、それらのプロパティにアクセスする方法Cは、ドット ( .) 演算子を使用することです。Objective-C は、s を使用するときにこの演算子を維持しますstruct。便宜上、矢印 ( ->) 演算子 (言及した 2 つの同等の式) を使用して逆参照とドット操作を実行できます。Objective-C は、structs にアクセスするときにもこれを保持します。

ただし、あなたの例のObjective-CFractionは、おそらく少なくとも type のポインターでありid、これは単にクラス名であり、内部のそのクラスのインスタンスへのポインターです。NSObjectまたはのサブクラスである可能性も非常に高いNSProxyです。これらの Objective-C クラスは、C構造体の上に事前定義された操作のレイヤー全体があるという点で特別です (本当に掘り下げたい場合は、Objective-C ランタイム リファレンスを参照してください)。また、Objective-C クラスは常に pointerであることに注意してください。

最も基本的な操作の 1 つがobjc_msgSend. これらのタイプのオブジェクトを操作する場合、Objective-C コンパイラは、ドット ( .) 演算子または角括弧構文 ( [object method]) をobjc_msgSendメソッド呼び出しとして解釈します。ここで実際に何が起こっているかについての詳細は、Obj-C ランタイムの開発を監督する Apple のエンジニアである Bill Bumgarner による一連の投稿を参照してください。

矢印 ( ->) 演算子は、実際には Objective-C オブジェクトで使用することは想定されていません。前述したように、Objective-C クラス インスタンスは C 構造体に追加の通信レイヤーが追加されたものですが、矢印を使用すると、その通信レイヤーは基本的にバイパスされます。たとえば、Xcode を開いて入力し[UIApplication sharedApplication]->、メソッド補完リストを表示すると、次のように表示されます。

矢印演算子を使用した場合の Obj-C オブジェクトの内部 ivar のリスト

ここでは、角かっこの構文 ( など) で通常アクセスする通常のフィールドの束を見ることができます[[UIApplication sharedApplication] delegate]。ただし、これらの特定の項目はC、それぞれの Objective-C プロパティの値を格納するフィールドです。

というわけで、大まかに次のように考えることができます。

C オブジェクトのドット演算子

  1. (実行時) フィールドの戻り値

C オブジェクトの矢印演算子 (ポインター)

  1. 逆参照ポインター
  2. フィールドの戻り値

Objective-C オブジェクトのドット演算子/角括弧 (ポインター)

  1. (コンパイル時) への呼び出しに置き換えますobjc_msgSend
  2. (実行時) Obj-C クラス定義を検索し、問題が発生した場合は例外をスローします
  3. 逆参照ポインター
  4. フィールドの戻り値

Objective-C オブジェクトの矢印演算子 (ポインター)

  1. (実行時) 逆参照ポインタ
  2. フィールドの戻り値

ここでは間違いなく単純化しすぎていますが、要約すると、矢印演算子は基本的にどちらの場合も同じことを行うように見えますが、ドット演算子は Objective-C では余分な/異なる意味を持っています。

于 2012-01-31T01:09:28.287 に答える
9

ドット表記は設計上の選択です。私たちは常に objc インスタンスへのポインタを扱っているので、設計者は、既存のプログラムを壊すことのない親しみやすいものを望んでいたと思います。ObjC 2 で導入されました - ほんの数年前です。それ以前は、メッセージには常に括弧を使用する必要がありました。

ただし、ドット表記は違いを生みます。これは直接アクセスではなく、メッセージです。

あれは:

obj.property = val;
// is the same as:
[obj setProperty:val];
// and not:
obj->property = val;


val = obj.property;
// is the same as:
val = [obj property];
// and not:
val = obj->property;

obj->ivarオブジェクトのメンバー (表示されている場合) へのポインターにアクセスするために、引き続き書き込みを行うことができます。

于 2012-01-31T00:18:11.470 に答える
5

最初の例でFractionは、構造体です。2 番目の例でFractionは、Objective-C クラスです (iOS では のサブクラスになる可能性がありますNSObject)。

C++はのオーバーロードを許可しませんoperator .。したがって、追加情報がなければ、表示されているドット表記は、C/C++ で定義またはオーバーロードされた演算子ではなく、Objective-C に統合された追加の言語構造であると推測できます。

たまたま、ドット表記は単に実装者がプロパティ アクセスの省略形として選択した設計機能であり、角かっこの getter と完全に同等です。

myObjCVar.prop == [myObjCVar prop];
于 2012-01-31T00:18:52.420 に答える
2

オブジェクトのドット演算子は、オブジェクトのプロパティにアクセスするための特別な構文です。舞台裏でプロパティのゲッターまたはセッターを呼び出します。したがって、たとえば、[@"hello" length]@"hello".lengthは同等です*。他のすべてのタイプでは、ドットは C ドットと同じで、矢印は常に同じです。

* 注: アクセサー メソッドは常にプロパティと同じ名前になるとは限りません。それが宣言されたプロパティであり、宣言が特別な getter または setter メソッドを指定している場合、そのメソッドが代わりに使用されます。

于 2012-01-31T00:18:30.473 に答える
1

C のドットと矢印の表記は、Objective-C (の厳密なスーパーセット) と同じです。区別する必要がある基本的な違いは、構造体と Objective-C オブジェクトの違いだと思います。

Objective-C のオブジェクトに使用されるドット表記は、Objective-C 2.0 で導入されたプロパティに使用されます。ただし、構造体の場合、Objective-C と C の間の -> およびドット表記は同じです。

于 2012-01-31T00:22:00.047 に答える